From c82e3b3b2de0633317ec8fc85925e91422820597 Mon Sep 17 00:00:00 2001 From: "Benjamin J. Culkin" Date: Sun, 8 Oct 2017 22:39:59 -0300 Subject: Start splitting into maven modules --- BJC-Utils2/.amateras | 11 - BJC-Utils2/.classpath | 32 -- BJC-Utils2/.gitignore | 1 - BJC-Utils2/.project | 23 - .../.settings/org.eclipse.core.resources.prefs | 5 - BJC-Utils2/.settings/org.eclipse.jdt.core.prefs | 106 ---- BJC-Utils2/.settings/org.eclipse.m2e.core.prefs | 4 - BJC-Utils2/data/BidiMirrorDB.txt | 606 --------------------- BJC-Utils2/data/formats.sprop | 160 ------ BJC-Utils2/data/regexes.sprop | 61 --- BJC-Utils2/docs/man5/BlockReaderCLI.5 | 8 - BJC-Utils2/pom.xml | 80 --- .../java/bjc/utils/examples/AbbrevMapTest.java | 59 -- .../java/bjc/utils/examples/BinarySearchTest.java | 123 ----- .../java/bjc/utils/examples/DelimSplitterTest.java | 504 ----------------- .../java/bjc/utils/examples/ShuntTest.java | 37 -- .../bjc/utils/examples/rangen/DiabloItemGen.java | 94 ---- .../examples/rangen/RandomStringExamples.java | 64 --- .../bjc/utils/examples/sample-ds-files/html.ds | 10 - .../bjc/utils/examples/sample-ds-files/json.ds | 23 - .../src/examples/java/bjc/utils/examples/test.tree | 13 - BJC-Utils2/src/main/java/bjc/utils/PropertyDB.java | 160 ------ .../src/main/java/bjc/utils/cli/CLICommander.java | 134 ----- .../src/main/java/bjc/utils/cli/Command.java | 39 -- .../main/java/bjc/utils/cli/CommandHandler.java | 24 - .../src/main/java/bjc/utils/cli/CommandHelp.java | 31 -- .../src/main/java/bjc/utils/cli/CommandMode.java | 72 --- .../main/java/bjc/utils/cli/DelegatingCommand.java | 64 --- .../main/java/bjc/utils/cli/GenericCommand.java | 84 --- .../java/bjc/utils/cli/GenericCommandMode.java | 469 ---------------- .../src/main/java/bjc/utils/cli/GenericHelp.java | 63 --- .../src/main/java/bjc/utils/cli/NullHelp.java | 20 - .../java/bjc/utils/cli/objects/BlockReaderCLI.java | 392 ------------- .../main/java/bjc/utils/cli/objects/Command.java | 87 --- .../main/java/bjc/utils/cli/objects/DefineCLI.java | 133 ----- .../bjc/utils/components/ComponentDescription.java | 135 ----- .../components/ComponentDescriptionFileParser.java | 65 --- .../components/ComponentDescriptionState.java | 144 ----- .../utils/components/FileComponentRepository.java | 181 ------ .../bjc/utils/components/IComponentRepository.java | 49 -- .../bjc/utils/components/IDescribedComponent.java | 64 --- .../main/java/bjc/utils/data/BooleanToggle.java | 76 --- .../main/java/bjc/utils/data/CircularIterator.java | 81 --- .../src/main/java/bjc/utils/data/Either.java | 173 ------ .../java/bjc/utils/data/GeneratingIterator.java | 53 -- .../src/main/java/bjc/utils/data/IHolder.java | 153 ------ BJC-Utils2/src/main/java/bjc/utils/data/IPair.java | 200 ------- BJC-Utils2/src/main/java/bjc/utils/data/ITree.java | 234 -------- .../src/main/java/bjc/utils/data/Identity.java | 118 ---- BJC-Utils2/src/main/java/bjc/utils/data/Lazy.java | 194 ------- .../src/main/java/bjc/utils/data/LazyPair.java | 240 -------- .../src/main/java/bjc/utils/data/ListHolder.java | 104 ---- .../src/main/java/bjc/utils/data/Option.java | 93 ---- BJC-Utils2/src/main/java/bjc/utils/data/Pair.java | 135 ----- .../main/java/bjc/utils/data/SingleIterator.java | 41 -- .../main/java/bjc/utils/data/SingleSupplier.java | 72 --- .../src/main/java/bjc/utils/data/Toggle.java | 35 -- .../bjc/utils/data/TopDownTransformIterator.java | 208 ------- .../bjc/utils/data/TopDownTransformResult.java | 34 -- .../java/bjc/utils/data/TransformIterator.java | 46 -- BJC-Utils2/src/main/java/bjc/utils/data/Tree.java | 390 ------------- .../src/main/java/bjc/utils/data/ValueToggle.java | 54 -- .../java/bjc/utils/data/internals/BoundLazy.java | 145 ----- .../bjc/utils/data/internals/BoundLazyPair.java | 199 ------- .../bjc/utils/data/internals/BoundListHolder.java | 68 --- .../utils/data/internals/HalfBoundLazyPair.java | 149 ----- .../java/bjc/utils/data/internals/WrappedLazy.java | 62 --- .../bjc/utils/data/internals/WrappedOption.java | 76 --- .../src/main/java/bjc/utils/esodata/AbbrevMap.java | 227 -------- .../src/main/java/bjc/utils/esodata/Directory.java | 106 ---- .../main/java/bjc/utils/esodata/DoubleTape.java | 258 --------- .../main/java/bjc/utils/esodata/PushdownMap.java | 148 ----- .../main/java/bjc/utils/esodata/QueueStack.java | 88 --- .../java/bjc/utils/esodata/SimpleDirectory.java | 95 ---- .../main/java/bjc/utils/esodata/SimpleStack.java | 88 --- .../main/java/bjc/utils/esodata/SingleTape.java | 255 --------- .../java/bjc/utils/esodata/SpaghettiStack.java | 99 ---- .../src/main/java/bjc/utils/esodata/Stack.java | 459 ---------------- .../src/main/java/bjc/utils/esodata/Tape.java | 126 ----- .../main/java/bjc/utils/esodata/TapeChanger.java | 363 ------------ .../main/java/bjc/utils/esodata/TapeLibrary.java | 340 ------------ .../java/bjc/utils/esodata/UnifiedDirectory.java | 105 ---- .../utils/exceptions/FileNotChosenException.java | 31 -- .../utils/exceptions/PragmaFormatException.java | 31 -- .../utils/exceptions/UnknownPragmaException.java | 25 - .../main/java/bjc/utils/funcdata/ExtendedMap.java | 127 ----- .../java/bjc/utils/funcdata/FunctionalList.java | 423 -------------- .../java/bjc/utils/funcdata/FunctionalMap.java | 175 ------ .../utils/funcdata/FunctionalStringTokenizer.java | 159 ------ .../src/main/java/bjc/utils/funcdata/IList.java | 416 -------------- .../src/main/java/bjc/utils/funcdata/IMap.java | 188 ------- .../main/java/bjc/utils/funcdata/SentryList.java | 41 -- .../bjc/utils/funcdata/TransformedValueMap.java | 102 ---- .../bjc/utils/funcdata/bst/BinarySearchTree.java | 221 -------- .../utils/funcdata/bst/BinarySearchTreeLeaf.java | 119 ---- .../utils/funcdata/bst/BinarySearchTreeNode.java | 287 ---------- .../utils/funcdata/bst/DirectedWalkFunction.java | 49 -- .../java/bjc/utils/funcdata/bst/ITreePart.java | 96 ---- .../funcdata/bst/TreeLinearizationMethod.java | 25 - .../java/bjc/utils/funcdata/theory/Bifunctor.java | 139 ----- .../java/bjc/utils/funcdata/theory/Functor.java | 39 -- .../src/main/java/bjc/utils/functypes/ID.java | 20 - .../java/bjc/utils/functypes/ListFlattener.java | 17 - .../java/bjc/utils/funcutils/CollectorUtils.java | 39 -- .../bjc/utils/funcutils/CompoundCollector.java | 89 --- .../main/java/bjc/utils/funcutils/EnumUtils.java | 63 --- .../main/java/bjc/utils/funcutils/FileUtils.java | 40 -- .../main/java/bjc/utils/funcutils/FuncUtils.java | 76 --- .../bjc/utils/funcutils/FunctionalFileVisitor.java | 36 -- .../bjc/utils/funcutils/GroupPartIteration.java | 62 --- .../main/java/bjc/utils/funcutils/IBuilder.java | 31 -- .../main/java/bjc/utils/funcutils/Isomorphism.java | 60 -- .../main/java/bjc/utils/funcutils/LambdaLock.java | 105 ---- .../main/java/bjc/utils/funcutils/ListUtils.java | 294 ---------- .../main/java/bjc/utils/funcutils/NumberUtils.java | 69 --- .../main/java/bjc/utils/funcutils/StringUtils.java | 196 ------- .../main/java/bjc/utils/funcutils/TreeUtils.java | 56 -- .../main/java/bjc/utils/funcutils/TriConsumer.java | 31 -- .../src/main/java/bjc/utils/gen/RandomGrammar.java | 69 --- .../main/java/bjc/utils/gen/WeightedGrammar.java | 573 ------------------- .../main/java/bjc/utils/gen/WeightedRandom.java | 112 ---- .../main/java/bjc/utils/graph/AdjacencyMap.java | 216 -------- BJC-Utils2/src/main/java/bjc/utils/graph/Edge.java | 112 ---- .../src/main/java/bjc/utils/graph/Graph.java | 267 --------- .../java/bjc/utils/gui/ExtensionFileFilter.java | 56 -- .../src/main/java/bjc/utils/gui/SimpleDialogs.java | 269 --------- .../main/java/bjc/utils/gui/SimpleFileChooser.java | 198 ------- .../java/bjc/utils/gui/SimpleInternalDialogs.java | 208 ------- .../java/bjc/utils/gui/SimpleInternalFrame.java | 40 -- .../src/main/java/bjc/utils/gui/SimpleJList.java | 49 -- .../java/bjc/utils/gui/SimpleTitledBorder.java | 25 - .../java/bjc/utils/gui/TextAreaOutputStream.java | 35 -- .../bjc/utils/gui/awt/ExtensionFileFilter.java | 50 -- .../java/bjc/utils/gui/awt/SimpleFileDialog.java | 144 ----- .../java/bjc/utils/gui/layout/AutosizeLayout.java | 22 - .../main/java/bjc/utils/gui/layout/HLayout.java | 25 - .../main/java/bjc/utils/gui/layout/VLayout.java | 25 - .../bjc/utils/gui/panels/DropdownListPanel.java | 73 --- .../bjc/utils/gui/panels/FormattedInputPanel.java | 66 --- .../bjc/utils/gui/panels/HolderOutputPanel.java | 79 --- .../bjc/utils/gui/panels/ListParameterPanel.java | 133 ----- .../bjc/utils/gui/panels/SimpleInputPanel.java | 45 -- .../java/bjc/utils/gui/panels/SimpleListPanel.java | 93 ---- .../bjc/utils/gui/panels/SimpleSpinnerPanel.java | 42 -- .../bjc/utils/gui/panels/SliderInputPanel.java | 187 ------- .../java/bjc/utils/gui/panels/package-info.java | 5 - .../main/java/bjc/utils/ioutils/CLFormatter.java | 531 ------------------ .../main/java/bjc/utils/ioutils/CLParameters.java | 109 ---- .../main/java/bjc/utils/ioutils/NumberUtils.java | 405 -------------- .../src/main/java/bjc/utils/ioutils/Prompter.java | 47 -- .../java/bjc/utils/ioutils/RegexStringEditor.java | 230 -------- .../bjc/utils/ioutils/RuleBasedConfigReader.java | 265 --------- .../bjc/utils/ioutils/RuleBasedReaderPragmas.java | 100 ---- .../java/bjc/utils/ioutils/SimpleProperties.java | 170 ------ .../main/java/bjc/utils/ioutils/blocks/Block.java | 88 --- .../java/bjc/utils/ioutils/blocks/BlockReader.java | 73 --- .../bjc/utils/ioutils/blocks/BlockReaders.java | 81 --- .../bjc/utils/ioutils/blocks/BoundBlockReader.java | 61 --- .../utils/ioutils/blocks/FilteredBlockReader.java | 97 ---- .../ioutils/blocks/FlatMappedBlockReader.java | 86 --- .../utils/ioutils/blocks/LayeredBlockReader.java | 81 --- .../utils/ioutils/blocks/MappedBlockReader.java | 54 -- .../utils/ioutils/blocks/PushbackBlockReader.java | 106 ---- .../utils/ioutils/blocks/SerialBlockReader.java | 102 ---- .../utils/ioutils/blocks/SimpleBlockReader.java | 115 ---- .../utils/ioutils/blocks/ToggledBlockReader.java | 63 --- .../utils/ioutils/blocks/TriggeredBlockReader.java | 70 --- .../java/bjc/utils/parserutils/DoubleMatcher.java | 46 -- .../java/bjc/utils/parserutils/IPrecedent.java | 28 - .../bjc/utils/parserutils/ParserException.java | 36 -- .../java/bjc/utils/parserutils/ShuntingYard.java | 274 ---------- .../java/bjc/utils/parserutils/StringDescaper.java | 242 -------- .../bjc/utils/parserutils/TokenTransformer.java | 131 ----- .../java/bjc/utils/parserutils/TokenUtils.java | 303 ----------- .../bjc/utils/parserutils/TreeConstructor.java | 125 ----- .../utils/parserutils/defines/IteratedDefine.java | 48 -- .../utils/parserutils/defines/SimpleDefine.java | 23 - .../parserutils/delims/DelimiterException.java | 21 - .../utils/parserutils/delims/DelimiterGroup.java | 593 -------------------- .../bjc/utils/parserutils/delims/RegexCloser.java | 33 -- .../bjc/utils/parserutils/delims/RegexOpener.java | 54 -- .../delims/SequenceCharacteristics.java | 93 ---- .../parserutils/delims/SequenceDelimiter.java | 371 ------------- .../utils/parserutils/delims/StringDelimiter.java | 31 -- .../parserutils/splitter/ChainTokenSplitter.java | 50 -- .../splitter/ConfigurableTokenSplitter.java | 122 ----- .../splitter/ExcludingTokenSplitter.java | 71 --- .../splitter/FilteredTokenSplitter.java | 37 -- .../parserutils/splitter/SimpleTokenSplitter.java | 46 -- .../utils/parserutils/splitter/TokenSplitter.java | 21 - .../splitter/TransformTokenSplitter.java | 38 -- .../bjc/utils/test/parserutils/TokenUtilsTest.java | 152 ------ base/.amateras | 11 + base/.gitignore | 1 + base/data/BidiMirrorDB.txt | 606 +++++++++++++++++++++ base/data/formats.sprop | 160 ++++++ base/data/regexes.sprop | 61 +++ base/docs/man5/BlockReaderCLI.5 | 8 + base/pom.xml | 87 +++ .../java/bjc/utils/examples/AbbrevMapTest.java | 59 ++ .../java/bjc/utils/examples/BinarySearchTest.java | 123 +++++ .../java/bjc/utils/examples/DelimSplitterTest.java | 504 +++++++++++++++++ .../java/bjc/utils/examples/ShuntTest.java | 37 ++ .../bjc/utils/examples/rangen/DiabloItemGen.java | 94 ++++ .../examples/rangen/RandomStringExamples.java | 64 +++ .../bjc/utils/examples/sample-ds-files/html.ds | 10 + .../bjc/utils/examples/sample-ds-files/json.ds | 23 + .../src/examples/java/bjc/utils/examples/test.tree | 13 + base/src/main/java/bjc/utils/PropertyDB.java | 160 ++++++ base/src/main/java/bjc/utils/cli/CLICommander.java | 134 +++++ base/src/main/java/bjc/utils/cli/Command.java | 39 ++ .../main/java/bjc/utils/cli/CommandHandler.java | 24 + base/src/main/java/bjc/utils/cli/CommandHelp.java | 31 ++ base/src/main/java/bjc/utils/cli/CommandMode.java | 72 +++ .../main/java/bjc/utils/cli/DelegatingCommand.java | 64 +++ .../main/java/bjc/utils/cli/GenericCommand.java | 84 +++ .../java/bjc/utils/cli/GenericCommandMode.java | 469 ++++++++++++++++ base/src/main/java/bjc/utils/cli/GenericHelp.java | 63 +++ base/src/main/java/bjc/utils/cli/NullHelp.java | 20 + .../java/bjc/utils/cli/objects/BlockReaderCLI.java | 392 +++++++++++++ .../main/java/bjc/utils/cli/objects/Command.java | 87 +++ .../main/java/bjc/utils/cli/objects/DefineCLI.java | 133 +++++ .../bjc/utils/components/ComponentDescription.java | 135 +++++ .../components/ComponentDescriptionFileParser.java | 65 +++ .../components/ComponentDescriptionState.java | 144 +++++ .../utils/components/FileComponentRepository.java | 181 ++++++ .../bjc/utils/components/IComponentRepository.java | 49 ++ .../bjc/utils/components/IDescribedComponent.java | 64 +++ .../main/java/bjc/utils/data/BooleanToggle.java | 76 +++ .../main/java/bjc/utils/data/CircularIterator.java | 81 +++ base/src/main/java/bjc/utils/data/Either.java | 173 ++++++ .../java/bjc/utils/data/GeneratingIterator.java | 53 ++ base/src/main/java/bjc/utils/data/IHolder.java | 153 ++++++ base/src/main/java/bjc/utils/data/IPair.java | 200 +++++++ base/src/main/java/bjc/utils/data/ITree.java | 234 ++++++++ base/src/main/java/bjc/utils/data/Identity.java | 118 ++++ base/src/main/java/bjc/utils/data/Lazy.java | 194 +++++++ base/src/main/java/bjc/utils/data/LazyPair.java | 240 ++++++++ base/src/main/java/bjc/utils/data/ListHolder.java | 104 ++++ base/src/main/java/bjc/utils/data/Option.java | 93 ++++ base/src/main/java/bjc/utils/data/Pair.java | 135 +++++ .../main/java/bjc/utils/data/SingleIterator.java | 41 ++ .../main/java/bjc/utils/data/SingleSupplier.java | 72 +++ base/src/main/java/bjc/utils/data/Toggle.java | 35 ++ .../bjc/utils/data/TopDownTransformIterator.java | 208 +++++++ .../bjc/utils/data/TopDownTransformResult.java | 34 ++ .../java/bjc/utils/data/TransformIterator.java | 46 ++ base/src/main/java/bjc/utils/data/Tree.java | 390 +++++++++++++ base/src/main/java/bjc/utils/data/ValueToggle.java | 54 ++ .../java/bjc/utils/data/internals/BoundLazy.java | 145 +++++ .../bjc/utils/data/internals/BoundLazyPair.java | 199 +++++++ .../bjc/utils/data/internals/BoundListHolder.java | 68 +++ .../utils/data/internals/HalfBoundLazyPair.java | 149 +++++ .../java/bjc/utils/data/internals/WrappedLazy.java | 62 +++ .../bjc/utils/data/internals/WrappedOption.java | 76 +++ .../src/main/java/bjc/utils/esodata/AbbrevMap.java | 227 ++++++++ .../src/main/java/bjc/utils/esodata/Directory.java | 106 ++++ .../main/java/bjc/utils/esodata/DoubleTape.java | 258 +++++++++ .../main/java/bjc/utils/esodata/PushdownMap.java | 148 +++++ .../main/java/bjc/utils/esodata/QueueStack.java | 88 +++ .../java/bjc/utils/esodata/SimpleDirectory.java | 95 ++++ .../main/java/bjc/utils/esodata/SimpleStack.java | 88 +++ .../main/java/bjc/utils/esodata/SingleTape.java | 255 +++++++++ .../java/bjc/utils/esodata/SpaghettiStack.java | 99 ++++ base/src/main/java/bjc/utils/esodata/Stack.java | 459 ++++++++++++++++ base/src/main/java/bjc/utils/esodata/Tape.java | 126 +++++ .../main/java/bjc/utils/esodata/TapeChanger.java | 363 ++++++++++++ .../main/java/bjc/utils/esodata/TapeLibrary.java | 340 ++++++++++++ .../java/bjc/utils/esodata/UnifiedDirectory.java | 105 ++++ .../utils/exceptions/FileNotChosenException.java | 31 ++ .../utils/exceptions/PragmaFormatException.java | 31 ++ .../utils/exceptions/UnknownPragmaException.java | 25 + .../main/java/bjc/utils/funcdata/ExtendedMap.java | 127 +++++ .../java/bjc/utils/funcdata/FunctionalList.java | 423 ++++++++++++++ .../java/bjc/utils/funcdata/FunctionalMap.java | 175 ++++++ .../utils/funcdata/FunctionalStringTokenizer.java | 159 ++++++ base/src/main/java/bjc/utils/funcdata/IList.java | 416 ++++++++++++++ base/src/main/java/bjc/utils/funcdata/IMap.java | 188 +++++++ .../main/java/bjc/utils/funcdata/SentryList.java | 41 ++ .../bjc/utils/funcdata/TransformedValueMap.java | 102 ++++ .../bjc/utils/funcdata/bst/BinarySearchTree.java | 221 ++++++++ .../utils/funcdata/bst/BinarySearchTreeLeaf.java | 119 ++++ .../utils/funcdata/bst/BinarySearchTreeNode.java | 287 ++++++++++ .../utils/funcdata/bst/DirectedWalkFunction.java | 49 ++ .../java/bjc/utils/funcdata/bst/ITreePart.java | 96 ++++ .../funcdata/bst/TreeLinearizationMethod.java | 25 + .../java/bjc/utils/funcdata/theory/Bifunctor.java | 139 +++++ .../java/bjc/utils/funcdata/theory/Functor.java | 39 ++ base/src/main/java/bjc/utils/functypes/ID.java | 20 + .../java/bjc/utils/functypes/ListFlattener.java | 17 + .../java/bjc/utils/funcutils/CollectorUtils.java | 39 ++ .../bjc/utils/funcutils/CompoundCollector.java | 89 +++ .../main/java/bjc/utils/funcutils/EnumUtils.java | 63 +++ .../main/java/bjc/utils/funcutils/FileUtils.java | 40 ++ .../main/java/bjc/utils/funcutils/FuncUtils.java | 76 +++ .../bjc/utils/funcutils/FunctionalFileVisitor.java | 36 ++ .../bjc/utils/funcutils/GroupPartIteration.java | 62 +++ .../main/java/bjc/utils/funcutils/IBuilder.java | 31 ++ .../main/java/bjc/utils/funcutils/Isomorphism.java | 60 ++ .../main/java/bjc/utils/funcutils/LambdaLock.java | 105 ++++ .../main/java/bjc/utils/funcutils/ListUtils.java | 294 ++++++++++ .../main/java/bjc/utils/funcutils/NumberUtils.java | 69 +++ .../main/java/bjc/utils/funcutils/StringUtils.java | 196 +++++++ .../main/java/bjc/utils/funcutils/TreeUtils.java | 56 ++ .../main/java/bjc/utils/funcutils/TriConsumer.java | 31 ++ .../src/main/java/bjc/utils/gen/RandomGrammar.java | 69 +++ .../main/java/bjc/utils/gen/WeightedGrammar.java | 573 +++++++++++++++++++ .../main/java/bjc/utils/gen/WeightedRandom.java | 112 ++++ .../main/java/bjc/utils/graph/AdjacencyMap.java | 216 ++++++++ base/src/main/java/bjc/utils/graph/Edge.java | 112 ++++ base/src/main/java/bjc/utils/graph/Graph.java | 267 +++++++++ .../java/bjc/utils/gui/ExtensionFileFilter.java | 56 ++ .../src/main/java/bjc/utils/gui/SimpleDialogs.java | 269 +++++++++ .../main/java/bjc/utils/gui/SimpleFileChooser.java | 198 +++++++ .../java/bjc/utils/gui/SimpleInternalDialogs.java | 208 +++++++ .../java/bjc/utils/gui/SimpleInternalFrame.java | 40 ++ base/src/main/java/bjc/utils/gui/SimpleJList.java | 49 ++ .../java/bjc/utils/gui/SimpleTitledBorder.java | 25 + .../java/bjc/utils/gui/TextAreaOutputStream.java | 35 ++ .../bjc/utils/gui/awt/ExtensionFileFilter.java | 50 ++ .../java/bjc/utils/gui/awt/SimpleFileDialog.java | 144 +++++ .../java/bjc/utils/gui/layout/AutosizeLayout.java | 22 + .../main/java/bjc/utils/gui/layout/HLayout.java | 25 + .../main/java/bjc/utils/gui/layout/VLayout.java | 25 + .../bjc/utils/gui/panels/DropdownListPanel.java | 73 +++ .../bjc/utils/gui/panels/FormattedInputPanel.java | 66 +++ .../bjc/utils/gui/panels/HolderOutputPanel.java | 79 +++ .../bjc/utils/gui/panels/ListParameterPanel.java | 133 +++++ .../bjc/utils/gui/panels/SimpleInputPanel.java | 45 ++ .../java/bjc/utils/gui/panels/SimpleListPanel.java | 93 ++++ .../bjc/utils/gui/panels/SimpleSpinnerPanel.java | 42 ++ .../bjc/utils/gui/panels/SliderInputPanel.java | 187 +++++++ .../java/bjc/utils/gui/panels/package-info.java | 5 + .../main/java/bjc/utils/ioutils/CLFormatter.java | 531 ++++++++++++++++++ .../main/java/bjc/utils/ioutils/CLParameters.java | 109 ++++ .../main/java/bjc/utils/ioutils/NumberUtils.java | 405 ++++++++++++++ base/src/main/java/bjc/utils/ioutils/Prompter.java | 47 ++ .../java/bjc/utils/ioutils/RegexStringEditor.java | 230 ++++++++ .../bjc/utils/ioutils/RuleBasedConfigReader.java | 265 +++++++++ .../bjc/utils/ioutils/RuleBasedReaderPragmas.java | 100 ++++ .../java/bjc/utils/ioutils/SimpleProperties.java | 170 ++++++ .../main/java/bjc/utils/ioutils/blocks/Block.java | 88 +++ .../java/bjc/utils/ioutils/blocks/BlockReader.java | 73 +++ .../bjc/utils/ioutils/blocks/BlockReaders.java | 81 +++ .../bjc/utils/ioutils/blocks/BoundBlockReader.java | 61 +++ .../utils/ioutils/blocks/FilteredBlockReader.java | 97 ++++ .../ioutils/blocks/FlatMappedBlockReader.java | 86 +++ .../utils/ioutils/blocks/LayeredBlockReader.java | 81 +++ .../utils/ioutils/blocks/MappedBlockReader.java | 54 ++ .../utils/ioutils/blocks/PushbackBlockReader.java | 106 ++++ .../utils/ioutils/blocks/SerialBlockReader.java | 102 ++++ .../utils/ioutils/blocks/SimpleBlockReader.java | 115 ++++ .../utils/ioutils/blocks/ToggledBlockReader.java | 63 +++ .../utils/ioutils/blocks/TriggeredBlockReader.java | 70 +++ .../java/bjc/utils/parserutils/DoubleMatcher.java | 46 ++ .../java/bjc/utils/parserutils/IPrecedent.java | 28 + .../bjc/utils/parserutils/ParserException.java | 36 ++ .../java/bjc/utils/parserutils/ShuntingYard.java | 274 ++++++++++ .../java/bjc/utils/parserutils/StringDescaper.java | 242 ++++++++ .../bjc/utils/parserutils/TokenTransformer.java | 131 +++++ .../java/bjc/utils/parserutils/TokenUtils.java | 303 +++++++++++ .../bjc/utils/parserutils/TreeConstructor.java | 125 +++++ .../utils/parserutils/defines/IteratedDefine.java | 48 ++ .../utils/parserutils/defines/SimpleDefine.java | 23 + .../parserutils/delims/DelimiterException.java | 21 + .../utils/parserutils/delims/DelimiterGroup.java | 593 ++++++++++++++++++++ .../bjc/utils/parserutils/delims/RegexCloser.java | 33 ++ .../bjc/utils/parserutils/delims/RegexOpener.java | 54 ++ .../delims/SequenceCharacteristics.java | 93 ++++ .../parserutils/delims/SequenceDelimiter.java | 371 +++++++++++++ .../utils/parserutils/delims/StringDelimiter.java | 31 ++ .../parserutils/splitter/ChainTokenSplitter.java | 50 ++ .../splitter/ConfigurableTokenSplitter.java | 122 +++++ .../splitter/ExcludingTokenSplitter.java | 71 +++ .../splitter/FilteredTokenSplitter.java | 37 ++ .../parserutils/splitter/SimpleTokenSplitter.java | 46 ++ .../utils/parserutils/splitter/TokenSplitter.java | 21 + .../splitter/TransformTokenSplitter.java | 38 ++ .../bjc/utils/test/parserutils/TokenUtilsTest.java | 152 ++++++ pom.xml | 13 + todos.txt | 4 + 381 files changed, 23596 insertions(+), 23742 deletions(-) delete mode 100644 BJC-Utils2/.amateras delete mode 100644 BJC-Utils2/.classpath delete mode 100644 BJC-Utils2/.gitignore delete mode 100644 BJC-Utils2/.project delete mode 100644 BJC-Utils2/.settings/org.eclipse.core.resources.prefs delete mode 100644 BJC-Utils2/.settings/org.eclipse.jdt.core.prefs delete mode 100644 BJC-Utils2/.settings/org.eclipse.m2e.core.prefs delete mode 100644 BJC-Utils2/data/BidiMirrorDB.txt delete mode 100644 BJC-Utils2/data/formats.sprop delete mode 100644 BJC-Utils2/data/regexes.sprop delete mode 100644 BJC-Utils2/docs/man5/BlockReaderCLI.5 delete mode 100644 BJC-Utils2/pom.xml delete mode 100644 BJC-Utils2/src/examples/java/bjc/utils/examples/AbbrevMapTest.java delete mode 100644 BJC-Utils2/src/examples/java/bjc/utils/examples/BinarySearchTest.java delete mode 100644 BJC-Utils2/src/examples/java/bjc/utils/examples/DelimSplitterTest.java delete mode 100644 BJC-Utils2/src/examples/java/bjc/utils/examples/ShuntTest.java delete mode 100644 BJC-Utils2/src/examples/java/bjc/utils/examples/rangen/DiabloItemGen.java delete mode 100644 BJC-Utils2/src/examples/java/bjc/utils/examples/rangen/RandomStringExamples.java delete mode 100644 BJC-Utils2/src/examples/java/bjc/utils/examples/sample-ds-files/html.ds delete mode 100644 BJC-Utils2/src/examples/java/bjc/utils/examples/sample-ds-files/json.ds delete mode 100644 BJC-Utils2/src/examples/java/bjc/utils/examples/test.tree delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/PropertyDB.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/CLICommander.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/Command.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/CommandHandler.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/CommandHelp.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/CommandMode.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/DelegatingCommand.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommand.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommandMode.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/GenericHelp.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/NullHelp.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/cli/objects/DefineCLI.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescription.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionState.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/components/FileComponentRepository.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/components/IComponentRepository.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/components/IDescribedComponent.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/BooleanToggle.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/CircularIterator.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/Either.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/GeneratingIterator.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/IHolder.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/IPair.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/ITree.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/Identity.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/Lazy.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/LazyPair.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/ListHolder.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/Option.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/Pair.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/SingleIterator.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/SingleSupplier.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/Toggle.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/TopDownTransformIterator.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/TopDownTransformResult.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/TransformIterator.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/Tree.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/ValueToggle.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundLazy.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundLazyPair.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundListHolder.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/internals/HalfBoundLazyPair.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/internals/WrappedLazy.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/internals/WrappedOption.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/AbbrevMap.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/Directory.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/DoubleTape.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/PushdownMap.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/QueueStack.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/SimpleDirectory.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/SimpleStack.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/SingleTape.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/SpaghettiStack.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/Stack.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/Tape.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/TapeChanger.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/TapeLibrary.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/esodata/UnifiedDirectory.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/exceptions/FileNotChosenException.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/exceptions/PragmaFormatException.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/exceptions/UnknownPragmaException.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/ExtendedMap.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalList.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/IList.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/IMap.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/SentryList.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/TransformedValueMap.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTree.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeLeaf.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeNode.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/DirectedWalkFunction.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/ITreePart.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/TreeLinearizationMethod.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Functor.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/functypes/ID.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/functypes/ListFlattener.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/CollectorUtils.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/CompoundCollector.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/EnumUtils.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/FileUtils.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/FuncUtils.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/GroupPartIteration.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/IBuilder.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/Isomorphism.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/LambdaLock.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/NumberUtils.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/StringUtils.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/TreeUtils.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcutils/TriConsumer.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gen/RandomGrammar.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gen/WeightedGrammar.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gen/WeightedRandom.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/graph/AdjacencyMap.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/graph/Edge.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/graph/Graph.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/ExtensionFileFilter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/SimpleDialogs.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/SimpleFileChooser.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/SimpleInternalDialogs.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/SimpleInternalFrame.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/SimpleJList.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/SimpleTitledBorder.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/TextAreaOutputStream.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/layout/AutosizeLayout.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/layout/HLayout.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/layout/VLayout.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/panels/DropdownListPanel.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/panels/FormattedInputPanel.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/panels/HolderOutputPanel.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/panels/ListParameterPanel.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleInputPanel.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleListPanel.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleSpinnerPanel.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/panels/SliderInputPanel.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/gui/panels/package-info.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/CLFormatter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/CLParameters.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/NumberUtils.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/Prompter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/RegexStringEditor.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/SimpleProperties.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BoundBlockReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FlatMappedBlockReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/ToggledBlockReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/IPrecedent.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/ParserException.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDescaper.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/defines/IteratedDefine.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/defines/SimpleDefine.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterException.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexCloser.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceCharacteristics.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/StringDelimiter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ChainTokenSplitter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ConfigurableTokenSplitter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ExcludingTokenSplitter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/FilteredTokenSplitter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/TokenSplitter.java delete mode 100644 BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/TransformTokenSplitter.java delete mode 100644 BJC-Utils2/src/test/java/bjc/utils/test/parserutils/TokenUtilsTest.java create mode 100644 base/.amateras create mode 100644 base/.gitignore create mode 100644 base/data/BidiMirrorDB.txt create mode 100644 base/data/formats.sprop create mode 100644 base/data/regexes.sprop create mode 100644 base/docs/man5/BlockReaderCLI.5 create mode 100644 base/pom.xml create mode 100644 base/src/examples/java/bjc/utils/examples/AbbrevMapTest.java create mode 100644 base/src/examples/java/bjc/utils/examples/BinarySearchTest.java create mode 100644 base/src/examples/java/bjc/utils/examples/DelimSplitterTest.java create mode 100644 base/src/examples/java/bjc/utils/examples/ShuntTest.java create mode 100644 base/src/examples/java/bjc/utils/examples/rangen/DiabloItemGen.java create mode 100644 base/src/examples/java/bjc/utils/examples/rangen/RandomStringExamples.java create mode 100644 base/src/examples/java/bjc/utils/examples/sample-ds-files/html.ds create mode 100644 base/src/examples/java/bjc/utils/examples/sample-ds-files/json.ds create mode 100644 base/src/examples/java/bjc/utils/examples/test.tree create mode 100644 base/src/main/java/bjc/utils/PropertyDB.java create mode 100644 base/src/main/java/bjc/utils/cli/CLICommander.java create mode 100644 base/src/main/java/bjc/utils/cli/Command.java create mode 100644 base/src/main/java/bjc/utils/cli/CommandHandler.java create mode 100644 base/src/main/java/bjc/utils/cli/CommandHelp.java create mode 100644 base/src/main/java/bjc/utils/cli/CommandMode.java create mode 100644 base/src/main/java/bjc/utils/cli/DelegatingCommand.java create mode 100644 base/src/main/java/bjc/utils/cli/GenericCommand.java create mode 100644 base/src/main/java/bjc/utils/cli/GenericCommandMode.java create mode 100644 base/src/main/java/bjc/utils/cli/GenericHelp.java create mode 100644 base/src/main/java/bjc/utils/cli/NullHelp.java create mode 100644 base/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java create mode 100644 base/src/main/java/bjc/utils/cli/objects/Command.java create mode 100644 base/src/main/java/bjc/utils/cli/objects/DefineCLI.java create mode 100644 base/src/main/java/bjc/utils/components/ComponentDescription.java create mode 100644 base/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java create mode 100644 base/src/main/java/bjc/utils/components/ComponentDescriptionState.java create mode 100644 base/src/main/java/bjc/utils/components/FileComponentRepository.java create mode 100644 base/src/main/java/bjc/utils/components/IComponentRepository.java create mode 100644 base/src/main/java/bjc/utils/components/IDescribedComponent.java create mode 100644 base/src/main/java/bjc/utils/data/BooleanToggle.java create mode 100644 base/src/main/java/bjc/utils/data/CircularIterator.java create mode 100644 base/src/main/java/bjc/utils/data/Either.java create mode 100644 base/src/main/java/bjc/utils/data/GeneratingIterator.java create mode 100644 base/src/main/java/bjc/utils/data/IHolder.java create mode 100644 base/src/main/java/bjc/utils/data/IPair.java create mode 100644 base/src/main/java/bjc/utils/data/ITree.java create mode 100644 base/src/main/java/bjc/utils/data/Identity.java create mode 100644 base/src/main/java/bjc/utils/data/Lazy.java create mode 100644 base/src/main/java/bjc/utils/data/LazyPair.java create mode 100644 base/src/main/java/bjc/utils/data/ListHolder.java create mode 100644 base/src/main/java/bjc/utils/data/Option.java create mode 100644 base/src/main/java/bjc/utils/data/Pair.java create mode 100644 base/src/main/java/bjc/utils/data/SingleIterator.java create mode 100644 base/src/main/java/bjc/utils/data/SingleSupplier.java create mode 100644 base/src/main/java/bjc/utils/data/Toggle.java create mode 100644 base/src/main/java/bjc/utils/data/TopDownTransformIterator.java create mode 100644 base/src/main/java/bjc/utils/data/TopDownTransformResult.java create mode 100644 base/src/main/java/bjc/utils/data/TransformIterator.java create mode 100644 base/src/main/java/bjc/utils/data/Tree.java create mode 100644 base/src/main/java/bjc/utils/data/ValueToggle.java create mode 100644 base/src/main/java/bjc/utils/data/internals/BoundLazy.java create mode 100644 base/src/main/java/bjc/utils/data/internals/BoundLazyPair.java create mode 100644 base/src/main/java/bjc/utils/data/internals/BoundListHolder.java create mode 100644 base/src/main/java/bjc/utils/data/internals/HalfBoundLazyPair.java create mode 100644 base/src/main/java/bjc/utils/data/internals/WrappedLazy.java create mode 100644 base/src/main/java/bjc/utils/data/internals/WrappedOption.java create mode 100644 base/src/main/java/bjc/utils/esodata/AbbrevMap.java create mode 100644 base/src/main/java/bjc/utils/esodata/Directory.java create mode 100644 base/src/main/java/bjc/utils/esodata/DoubleTape.java create mode 100644 base/src/main/java/bjc/utils/esodata/PushdownMap.java create mode 100644 base/src/main/java/bjc/utils/esodata/QueueStack.java create mode 100644 base/src/main/java/bjc/utils/esodata/SimpleDirectory.java create mode 100644 base/src/main/java/bjc/utils/esodata/SimpleStack.java create mode 100644 base/src/main/java/bjc/utils/esodata/SingleTape.java create mode 100644 base/src/main/java/bjc/utils/esodata/SpaghettiStack.java create mode 100644 base/src/main/java/bjc/utils/esodata/Stack.java create mode 100644 base/src/main/java/bjc/utils/esodata/Tape.java create mode 100644 base/src/main/java/bjc/utils/esodata/TapeChanger.java create mode 100644 base/src/main/java/bjc/utils/esodata/TapeLibrary.java create mode 100644 base/src/main/java/bjc/utils/esodata/UnifiedDirectory.java create mode 100644 base/src/main/java/bjc/utils/exceptions/FileNotChosenException.java create mode 100644 base/src/main/java/bjc/utils/exceptions/PragmaFormatException.java create mode 100644 base/src/main/java/bjc/utils/exceptions/UnknownPragmaException.java create mode 100644 base/src/main/java/bjc/utils/funcdata/ExtendedMap.java create mode 100644 base/src/main/java/bjc/utils/funcdata/FunctionalList.java create mode 100644 base/src/main/java/bjc/utils/funcdata/FunctionalMap.java create mode 100644 base/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java create mode 100644 base/src/main/java/bjc/utils/funcdata/IList.java create mode 100644 base/src/main/java/bjc/utils/funcdata/IMap.java create mode 100644 base/src/main/java/bjc/utils/funcdata/SentryList.java create mode 100644 base/src/main/java/bjc/utils/funcdata/TransformedValueMap.java create mode 100644 base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTree.java create mode 100644 base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeLeaf.java create mode 100644 base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeNode.java create mode 100644 base/src/main/java/bjc/utils/funcdata/bst/DirectedWalkFunction.java create mode 100644 base/src/main/java/bjc/utils/funcdata/bst/ITreePart.java create mode 100644 base/src/main/java/bjc/utils/funcdata/bst/TreeLinearizationMethod.java create mode 100644 base/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java create mode 100644 base/src/main/java/bjc/utils/funcdata/theory/Functor.java create mode 100644 base/src/main/java/bjc/utils/functypes/ID.java create mode 100644 base/src/main/java/bjc/utils/functypes/ListFlattener.java create mode 100644 base/src/main/java/bjc/utils/funcutils/CollectorUtils.java create mode 100644 base/src/main/java/bjc/utils/funcutils/CompoundCollector.java create mode 100644 base/src/main/java/bjc/utils/funcutils/EnumUtils.java create mode 100644 base/src/main/java/bjc/utils/funcutils/FileUtils.java create mode 100644 base/src/main/java/bjc/utils/funcutils/FuncUtils.java create mode 100644 base/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java create mode 100644 base/src/main/java/bjc/utils/funcutils/GroupPartIteration.java create mode 100644 base/src/main/java/bjc/utils/funcutils/IBuilder.java create mode 100644 base/src/main/java/bjc/utils/funcutils/Isomorphism.java create mode 100644 base/src/main/java/bjc/utils/funcutils/LambdaLock.java create mode 100644 base/src/main/java/bjc/utils/funcutils/ListUtils.java create mode 100644 base/src/main/java/bjc/utils/funcutils/NumberUtils.java create mode 100644 base/src/main/java/bjc/utils/funcutils/StringUtils.java create mode 100644 base/src/main/java/bjc/utils/funcutils/TreeUtils.java create mode 100644 base/src/main/java/bjc/utils/funcutils/TriConsumer.java create mode 100644 base/src/main/java/bjc/utils/gen/RandomGrammar.java create mode 100644 base/src/main/java/bjc/utils/gen/WeightedGrammar.java create mode 100644 base/src/main/java/bjc/utils/gen/WeightedRandom.java create mode 100644 base/src/main/java/bjc/utils/graph/AdjacencyMap.java create mode 100644 base/src/main/java/bjc/utils/graph/Edge.java create mode 100644 base/src/main/java/bjc/utils/graph/Graph.java create mode 100644 base/src/main/java/bjc/utils/gui/ExtensionFileFilter.java create mode 100644 base/src/main/java/bjc/utils/gui/SimpleDialogs.java create mode 100644 base/src/main/java/bjc/utils/gui/SimpleFileChooser.java create mode 100644 base/src/main/java/bjc/utils/gui/SimpleInternalDialogs.java create mode 100644 base/src/main/java/bjc/utils/gui/SimpleInternalFrame.java create mode 100644 base/src/main/java/bjc/utils/gui/SimpleJList.java create mode 100644 base/src/main/java/bjc/utils/gui/SimpleTitledBorder.java create mode 100644 base/src/main/java/bjc/utils/gui/TextAreaOutputStream.java create mode 100644 base/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java create mode 100644 base/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java create mode 100644 base/src/main/java/bjc/utils/gui/layout/AutosizeLayout.java create mode 100644 base/src/main/java/bjc/utils/gui/layout/HLayout.java create mode 100644 base/src/main/java/bjc/utils/gui/layout/VLayout.java create mode 100644 base/src/main/java/bjc/utils/gui/panels/DropdownListPanel.java create mode 100644 base/src/main/java/bjc/utils/gui/panels/FormattedInputPanel.java create mode 100644 base/src/main/java/bjc/utils/gui/panels/HolderOutputPanel.java create mode 100644 base/src/main/java/bjc/utils/gui/panels/ListParameterPanel.java create mode 100644 base/src/main/java/bjc/utils/gui/panels/SimpleInputPanel.java create mode 100644 base/src/main/java/bjc/utils/gui/panels/SimpleListPanel.java create mode 100644 base/src/main/java/bjc/utils/gui/panels/SimpleSpinnerPanel.java create mode 100644 base/src/main/java/bjc/utils/gui/panels/SliderInputPanel.java create mode 100644 base/src/main/java/bjc/utils/gui/panels/package-info.java create mode 100644 base/src/main/java/bjc/utils/ioutils/CLFormatter.java create mode 100644 base/src/main/java/bjc/utils/ioutils/CLParameters.java create mode 100644 base/src/main/java/bjc/utils/ioutils/NumberUtils.java create mode 100644 base/src/main/java/bjc/utils/ioutils/Prompter.java create mode 100644 base/src/main/java/bjc/utils/ioutils/RegexStringEditor.java create mode 100644 base/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java create mode 100644 base/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java create mode 100644 base/src/main/java/bjc/utils/ioutils/SimpleProperties.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/Block.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/BoundBlockReader.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/FlatMappedBlockReader.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/ToggledBlockReader.java create mode 100644 base/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java create mode 100644 base/src/main/java/bjc/utils/parserutils/DoubleMatcher.java create mode 100644 base/src/main/java/bjc/utils/parserutils/IPrecedent.java create mode 100644 base/src/main/java/bjc/utils/parserutils/ParserException.java create mode 100644 base/src/main/java/bjc/utils/parserutils/ShuntingYard.java create mode 100644 base/src/main/java/bjc/utils/parserutils/StringDescaper.java create mode 100644 base/src/main/java/bjc/utils/parserutils/TokenTransformer.java create mode 100644 base/src/main/java/bjc/utils/parserutils/TokenUtils.java create mode 100644 base/src/main/java/bjc/utils/parserutils/TreeConstructor.java create mode 100644 base/src/main/java/bjc/utils/parserutils/defines/IteratedDefine.java create mode 100644 base/src/main/java/bjc/utils/parserutils/defines/SimpleDefine.java create mode 100644 base/src/main/java/bjc/utils/parserutils/delims/DelimiterException.java create mode 100644 base/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java create mode 100644 base/src/main/java/bjc/utils/parserutils/delims/RegexCloser.java create mode 100644 base/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java create mode 100644 base/src/main/java/bjc/utils/parserutils/delims/SequenceCharacteristics.java create mode 100644 base/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java create mode 100644 base/src/main/java/bjc/utils/parserutils/delims/StringDelimiter.java create mode 100644 base/src/main/java/bjc/utils/parserutils/splitter/ChainTokenSplitter.java create mode 100644 base/src/main/java/bjc/utils/parserutils/splitter/ConfigurableTokenSplitter.java create mode 100644 base/src/main/java/bjc/utils/parserutils/splitter/ExcludingTokenSplitter.java create mode 100644 base/src/main/java/bjc/utils/parserutils/splitter/FilteredTokenSplitter.java create mode 100644 base/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java create mode 100644 base/src/main/java/bjc/utils/parserutils/splitter/TokenSplitter.java create mode 100644 base/src/main/java/bjc/utils/parserutils/splitter/TransformTokenSplitter.java create mode 100644 base/src/test/java/bjc/utils/test/parserutils/TokenUtilsTest.java create mode 100644 pom.xml create mode 100644 todos.txt diff --git a/BJC-Utils2/.amateras b/BJC-Utils2/.amateras deleted file mode 100644 index b00142f..0000000 --- a/BJC-Utils2/.amateras +++ /dev/null @@ -1,11 +0,0 @@ -#EclipseHTMLEditor configuration file -#Sat Sep 24 10:15:28 EDT 2016 -validateDTD=true -useDTD=true -validateJSP=true -validateXML=true -validateJS=true -removeMarkers=false -root=/ -validateHTML=true -javaScripts= diff --git a/BJC-Utils2/.classpath b/BJC-Utils2/.classpath deleted file mode 100644 index f77e68c..0000000 --- a/BJC-Utils2/.classpath +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/BJC-Utils2/.gitignore b/BJC-Utils2/.gitignore deleted file mode 100644 index b83d222..0000000 --- a/BJC-Utils2/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target/ diff --git a/BJC-Utils2/.project b/BJC-Utils2/.project deleted file mode 100644 index e68911e..0000000 --- a/BJC-Utils2/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - BJC-Utils2 - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/BJC-Utils2/.settings/org.eclipse.core.resources.prefs b/BJC-Utils2/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 41654f9..0000000 --- a/BJC-Utils2/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 -encoding//src/test/java=UTF-8 -encoding/=UTF-8 -encoding/data=UTF-8 diff --git a/BJC-Utils2/.settings/org.eclipse.jdt.core.prefs b/BJC-Utils2/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index ddf2f32..0000000 --- a/BJC-Utils2/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,106 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled -org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore -org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull -org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= -org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable -org.eclipse.jdt.core.compiler.annotation.nullable.secondary= -org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.autoboxing=ignore -org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning -org.eclipse.jdt.core.compiler.problem.deadCode=warning -org.eclipse.jdt.core.compiler.problem.deprecation=warning -org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled -org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled -org.eclipse.jdt.core.compiler.problem.discouragedReference=warning -org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore -org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore -org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled -org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore -org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning -org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning -org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled -org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning -org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning -org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore -org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore -org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning -org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore -org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore -org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled -org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled -org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore -org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore -org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning -org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning -org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore -org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning -org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning -org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error -org.eclipse.jdt.core.compiler.problem.nullReference=warning -org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error -org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning -org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=info -org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore -org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning -org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore -org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore -org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore -org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning -org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning -org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore -org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning -org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore -org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore -org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore -org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled -org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning -org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled -org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled -org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled -org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore -org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning -org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled -org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning -org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning -org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore -org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning -org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore -org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore -org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled -org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedImport=warning -org.eclipse.jdt.core.compiler.problem.unusedLabel=warning -org.eclipse.jdt.core.compiler.problem.unusedLocal=warning -org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled -org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning -org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning -org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/BJC-Utils2/.settings/org.eclipse.m2e.core.prefs b/BJC-Utils2/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f..0000000 --- a/BJC-Utils2/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/BJC-Utils2/data/BidiMirrorDB.txt b/BJC-Utils2/data/BidiMirrorDB.txt deleted file mode 100644 index 68142c5..0000000 --- a/BJC-Utils2/data/BidiMirrorDB.txt +++ /dev/null @@ -1,606 +0,0 @@ -# BidiMirroring-9.0.0.txt -# Date: 2016-01-21, 22:00:00 GMT [KW, LI] -# © 2016 Unicode®, Inc. -# For terms of use, see http://www.unicode.org/terms_of_use.html -# -# Unicode Character Database -# For documentation, see http://www.unicode.org/reports/tr44/ -# -# Bidi_Mirroring_Glyph Property -# -# This file is an informative contributory data file in the -# Unicode Character Database. -# -# This data file lists characters that have the Bidi_Mirrored=Yes property -# value, for which there is another Unicode character that typically has a glyph -# that is the mirror image of the original character's glyph. -# -# The repertoire covered by the file is Unicode 9.0.0. -# -# The file contains a list of lines with mappings from one code point -# to another one for character-based mirroring. -# Note that for "real" mirroring, a rendering engine needs to select -# appropriate alternative glyphs, and that many Unicode characters do not -# have a mirror-image Unicode character. -# -# Each mapping line contains two fields, separated by a semicolon (';'). -# Each of the two fields contains a code point represented as a -# variable-length hexadecimal value with 4 to 6 digits. -# A comment indicates where the characters are "BEST FIT" mirroring. -# -# Code points for which Bidi_Mirrored=Yes, but for which no appropriate -# characters exist with mirrored glyphs, are -# listed as comments at the end of the file. -# -# Formally, the default value of the Bidi_Mirroring_Glyph property -# for each code point is , unless a mapping to -# some other character is specified in this data file. When a code -# point has the default value for the Bidi_Mirroring_Glyph property, -# that means that no other character exists whose glyph is suitable -# for character-based mirroring. -# -# For information on bidi mirroring, see UAX #9: Unicode Bidirectional Algorithm, -# at http://www.unicode.org/unicode/reports/tr9/ -# -# This file was originally created by Markus Scherer. -# Extended for Unicode 3.2, 4.0, 4.1, 5.0, 5.1, 5.2, and 6.0 by Ken Whistler, -# and for subsequent versions by Ken Whistler and Laurentiu Iancu. -# -# ############################################################ -# -# Property: Bidi_Mirroring_Glyph -# -# @missing: 0000..10FFFF; - -0028; 0029 # LEFT PARENTHESIS -0029; 0028 # RIGHT PARENTHESIS -003C; 003E # LESS-THAN SIGN -003E; 003C # GREATER-THAN SIGN -005B; 005D # LEFT SQUARE BRACKET -005D; 005B # RIGHT SQUARE BRACKET -007B; 007D # LEFT CURLY BRACKET -007D; 007B # RIGHT CURLY BRACKET -00AB; 00BB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK -00BB; 00AB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -0F3A; 0F3B # TIBETAN MARK GUG RTAGS GYON -0F3B; 0F3A # TIBETAN MARK GUG RTAGS GYAS -0F3C; 0F3D # TIBETAN MARK ANG KHANG GYON -0F3D; 0F3C # TIBETAN MARK ANG KHANG GYAS -169B; 169C # OGHAM FEATHER MARK -169C; 169B # OGHAM REVERSED FEATHER MARK -2039; 203A # SINGLE LEFT-POINTING ANGLE QUOTATION MARK -203A; 2039 # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK -2045; 2046 # LEFT SQUARE BRACKET WITH QUILL -2046; 2045 # RIGHT SQUARE BRACKET WITH QUILL -207D; 207E # SUPERSCRIPT LEFT PARENTHESIS -207E; 207D # SUPERSCRIPT RIGHT PARENTHESIS -208D; 208E # SUBSCRIPT LEFT PARENTHESIS -208E; 208D # SUBSCRIPT RIGHT PARENTHESIS -2208; 220B # ELEMENT OF -2209; 220C # NOT AN ELEMENT OF -220A; 220D # SMALL ELEMENT OF -220B; 2208 # CONTAINS AS MEMBER -220C; 2209 # DOES NOT CONTAIN AS MEMBER -220D; 220A # SMALL CONTAINS AS MEMBER -2215; 29F5 # DIVISION SLASH -223C; 223D # TILDE OPERATOR -223D; 223C # REVERSED TILDE -2243; 22CD # ASYMPTOTICALLY EQUAL TO -2252; 2253 # APPROXIMATELY EQUAL TO OR THE IMAGE OF -2253; 2252 # IMAGE OF OR APPROXIMATELY EQUAL TO -2254; 2255 # COLON EQUALS -2255; 2254 # EQUALS COLON -2264; 2265 # LESS-THAN OR EQUAL TO -2265; 2264 # GREATER-THAN OR EQUAL TO -2266; 2267 # LESS-THAN OVER EQUAL TO -2267; 2266 # GREATER-THAN OVER EQUAL TO -2268; 2269 # [BEST FIT] LESS-THAN BUT NOT EQUAL TO -2269; 2268 # [BEST FIT] GREATER-THAN BUT NOT EQUAL TO -226A; 226B # MUCH LESS-THAN -226B; 226A # MUCH GREATER-THAN -226E; 226F # [BEST FIT] NOT LESS-THAN -226F; 226E # [BEST FIT] NOT GREATER-THAN -2270; 2271 # [BEST FIT] NEITHER LESS-THAN NOR EQUAL TO -2271; 2270 # [BEST FIT] NEITHER GREATER-THAN NOR EQUAL TO -2272; 2273 # [BEST FIT] LESS-THAN OR EQUIVALENT TO -2273; 2272 # [BEST FIT] GREATER-THAN OR EQUIVALENT TO -2274; 2275 # [BEST FIT] NEITHER LESS-THAN NOR EQUIVALENT TO -2275; 2274 # [BEST FIT] NEITHER GREATER-THAN NOR EQUIVALENT TO -2276; 2277 # LESS-THAN OR GREATER-THAN -2277; 2276 # GREATER-THAN OR LESS-THAN -2278; 2279 # [BEST FIT] NEITHER LESS-THAN NOR GREATER-THAN -2279; 2278 # [BEST FIT] NEITHER GREATER-THAN NOR LESS-THAN -227A; 227B # PRECEDES -227B; 227A # SUCCEEDS -227C; 227D # PRECEDES OR EQUAL TO -227D; 227C # SUCCEEDS OR EQUAL TO -227E; 227F # [BEST FIT] PRECEDES OR EQUIVALENT TO -227F; 227E # [BEST FIT] SUCCEEDS OR EQUIVALENT TO -2280; 2281 # [BEST FIT] DOES NOT PRECEDE -2281; 2280 # [BEST FIT] DOES NOT SUCCEED -2282; 2283 # SUBSET OF -2283; 2282 # SUPERSET OF -2284; 2285 # [BEST FIT] NOT A SUBSET OF -2285; 2284 # [BEST FIT] NOT A SUPERSET OF -2286; 2287 # SUBSET OF OR EQUAL TO -2287; 2286 # SUPERSET OF OR EQUAL TO -2288; 2289 # [BEST FIT] NEITHER A SUBSET OF NOR EQUAL TO -2289; 2288 # [BEST FIT] NEITHER A SUPERSET OF NOR EQUAL TO -228A; 228B # [BEST FIT] SUBSET OF WITH NOT EQUAL TO -228B; 228A # [BEST FIT] SUPERSET OF WITH NOT EQUAL TO -228F; 2290 # SQUARE IMAGE OF -2290; 228F # SQUARE ORIGINAL OF -2291; 2292 # SQUARE IMAGE OF OR EQUAL TO -2292; 2291 # SQUARE ORIGINAL OF OR EQUAL TO -2298; 29B8 # CIRCLED DIVISION SLASH -22A2; 22A3 # RIGHT TACK -22A3; 22A2 # LEFT TACK -22A6; 2ADE # ASSERTION -22A8; 2AE4 # TRUE -22A9; 2AE3 # FORCES -22AB; 2AE5 # DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE -22B0; 22B1 # PRECEDES UNDER RELATION -22B1; 22B0 # SUCCEEDS UNDER RELATION -22B2; 22B3 # NORMAL SUBGROUP OF -22B3; 22B2 # CONTAINS AS NORMAL SUBGROUP -22B4; 22B5 # NORMAL SUBGROUP OF OR EQUAL TO -22B5; 22B4 # CONTAINS AS NORMAL SUBGROUP OR EQUAL TO -22B6; 22B7 # ORIGINAL OF -22B7; 22B6 # IMAGE OF -22C9; 22CA # LEFT NORMAL FACTOR SEMIDIRECT PRODUCT -22CA; 22C9 # RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT -22CB; 22CC # LEFT SEMIDIRECT PRODUCT -22CC; 22CB # RIGHT SEMIDIRECT PRODUCT -22CD; 2243 # REVERSED TILDE EQUALS -22D0; 22D1 # DOUBLE SUBSET -22D1; 22D0 # DOUBLE SUPERSET -22D6; 22D7 # LESS-THAN WITH DOT -22D7; 22D6 # GREATER-THAN WITH DOT -22D8; 22D9 # VERY MUCH LESS-THAN -22D9; 22D8 # VERY MUCH GREATER-THAN -22DA; 22DB # LESS-THAN EQUAL TO OR GREATER-THAN -22DB; 22DA # GREATER-THAN EQUAL TO OR LESS-THAN -22DC; 22DD # EQUAL TO OR LESS-THAN -22DD; 22DC # EQUAL TO OR GREATER-THAN -22DE; 22DF # EQUAL TO OR PRECEDES -22DF; 22DE # EQUAL TO OR SUCCEEDS -22E0; 22E1 # [BEST FIT] DOES NOT PRECEDE OR EQUAL -22E1; 22E0 # [BEST FIT] DOES NOT SUCCEED OR EQUAL -22E2; 22E3 # [BEST FIT] NOT SQUARE IMAGE OF OR EQUAL TO -22E3; 22E2 # [BEST FIT] NOT SQUARE ORIGINAL OF OR EQUAL TO -22E4; 22E5 # [BEST FIT] SQUARE IMAGE OF OR NOT EQUAL TO -22E5; 22E4 # [BEST FIT] SQUARE ORIGINAL OF OR NOT EQUAL TO -22E6; 22E7 # [BEST FIT] LESS-THAN BUT NOT EQUIVALENT TO -22E7; 22E6 # [BEST FIT] GREATER-THAN BUT NOT EQUIVALENT TO -22E8; 22E9 # [BEST FIT] PRECEDES BUT NOT EQUIVALENT TO -22E9; 22E8 # [BEST FIT] SUCCEEDS BUT NOT EQUIVALENT TO -22EA; 22EB # [BEST FIT] NOT NORMAL SUBGROUP OF -22EB; 22EA # [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP -22EC; 22ED # [BEST FIT] NOT NORMAL SUBGROUP OF OR EQUAL TO -22ED; 22EC # [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL -22F0; 22F1 # UP RIGHT DIAGONAL ELLIPSIS -22F1; 22F0 # DOWN RIGHT DIAGONAL ELLIPSIS -22F2; 22FA # ELEMENT OF WITH LONG HORIZONTAL STROKE -22F3; 22FB # ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE -22F4; 22FC # SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE -22F6; 22FD # ELEMENT OF WITH OVERBAR -22F7; 22FE # SMALL ELEMENT OF WITH OVERBAR -22FA; 22F2 # CONTAINS WITH LONG HORIZONTAL STROKE -22FB; 22F3 # CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE -22FC; 22F4 # SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE -22FD; 22F6 # CONTAINS WITH OVERBAR -22FE; 22F7 # SMALL CONTAINS WITH OVERBAR -2308; 2309 # LEFT CEILING -2309; 2308 # RIGHT CEILING -230A; 230B # LEFT FLOOR -230B; 230A # RIGHT FLOOR -2329; 232A # LEFT-POINTING ANGLE BRACKET -232A; 2329 # RIGHT-POINTING ANGLE BRACKET -2768; 2769 # MEDIUM LEFT PARENTHESIS ORNAMENT -2769; 2768 # MEDIUM RIGHT PARENTHESIS ORNAMENT -276A; 276B # MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT -276B; 276A # MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT -276C; 276D # MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT -276D; 276C # MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT -276E; 276F # HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT -276F; 276E # HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT -2770; 2771 # HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT -2771; 2770 # HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT -2772; 2773 # LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT -2773; 2772 # LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT -2774; 2775 # MEDIUM LEFT CURLY BRACKET ORNAMENT -2775; 2774 # MEDIUM RIGHT CURLY BRACKET ORNAMENT -27C3; 27C4 # OPEN SUBSET -27C4; 27C3 # OPEN SUPERSET -27C5; 27C6 # LEFT S-SHAPED BAG DELIMITER -27C6; 27C5 # RIGHT S-SHAPED BAG DELIMITER -27C8; 27C9 # REVERSE SOLIDUS PRECEDING SUBSET -27C9; 27C8 # SUPERSET PRECEDING SOLIDUS -27CB; 27CD # MATHEMATICAL RISING DIAGONAL -27CD; 27CB # MATHEMATICAL FALLING DIAGONAL -27D5; 27D6 # LEFT OUTER JOIN -27D6; 27D5 # RIGHT OUTER JOIN -27DD; 27DE # LONG RIGHT TACK -27DE; 27DD # LONG LEFT TACK -27E2; 27E3 # WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK -27E3; 27E2 # WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK -27E4; 27E5 # WHITE SQUARE WITH LEFTWARDS TICK -27E5; 27E4 # WHITE SQUARE WITH RIGHTWARDS TICK -27E6; 27E7 # MATHEMATICAL LEFT WHITE SQUARE BRACKET -27E7; 27E6 # MATHEMATICAL RIGHT WHITE SQUARE BRACKET -27E8; 27E9 # MATHEMATICAL LEFT ANGLE BRACKET -27E9; 27E8 # MATHEMATICAL RIGHT ANGLE BRACKET -27EA; 27EB # MATHEMATICAL LEFT DOUBLE ANGLE BRACKET -27EB; 27EA # MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET -27EC; 27ED # MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET -27ED; 27EC # MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET -27EE; 27EF # MATHEMATICAL LEFT FLATTENED PARENTHESIS -27EF; 27EE # MATHEMATICAL RIGHT FLATTENED PARENTHESIS -2983; 2984 # LEFT WHITE CURLY BRACKET -2984; 2983 # RIGHT WHITE CURLY BRACKET -2985; 2986 # LEFT WHITE PARENTHESIS -2986; 2985 # RIGHT WHITE PARENTHESIS -2987; 2988 # Z NOTATION LEFT IMAGE BRACKET -2988; 2987 # Z NOTATION RIGHT IMAGE BRACKET -2989; 298A # Z NOTATION LEFT BINDING BRACKET -298A; 2989 # Z NOTATION RIGHT BINDING BRACKET -298B; 298C # LEFT SQUARE BRACKET WITH UNDERBAR -298C; 298B # RIGHT SQUARE BRACKET WITH UNDERBAR -298D; 2990 # LEFT SQUARE BRACKET WITH TICK IN TOP CORNER -298E; 298F # RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER -298F; 298E # LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER -2990; 298D # RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER -2991; 2992 # LEFT ANGLE BRACKET WITH DOT -2992; 2991 # RIGHT ANGLE BRACKET WITH DOT -2993; 2994 # LEFT ARC LESS-THAN BRACKET -2994; 2993 # RIGHT ARC GREATER-THAN BRACKET -2995; 2996 # DOUBLE LEFT ARC GREATER-THAN BRACKET -2996; 2995 # DOUBLE RIGHT ARC LESS-THAN BRACKET -2997; 2998 # LEFT BLACK TORTOISE SHELL BRACKET -2998; 2997 # RIGHT BLACK TORTOISE SHELL BRACKET -29B8; 2298 # CIRCLED REVERSE SOLIDUS -29C0; 29C1 # CIRCLED LESS-THAN -29C1; 29C0 # CIRCLED GREATER-THAN -29C4; 29C5 # SQUARED RISING DIAGONAL SLASH -29C5; 29C4 # SQUARED FALLING DIAGONAL SLASH -29CF; 29D0 # LEFT TRIANGLE BESIDE VERTICAL BAR -29D0; 29CF # VERTICAL BAR BESIDE RIGHT TRIANGLE -29D1; 29D2 # BOWTIE WITH LEFT HALF BLACK -29D2; 29D1 # BOWTIE WITH RIGHT HALF BLACK -29D4; 29D5 # TIMES WITH LEFT HALF BLACK -29D5; 29D4 # TIMES WITH RIGHT HALF BLACK -29D8; 29D9 # LEFT WIGGLY FENCE -29D9; 29D8 # RIGHT WIGGLY FENCE -29DA; 29DB # LEFT DOUBLE WIGGLY FENCE -29DB; 29DA # RIGHT DOUBLE WIGGLY FENCE -29F5; 2215 # REVERSE SOLIDUS OPERATOR -29F8; 29F9 # BIG SOLIDUS -29F9; 29F8 # BIG REVERSE SOLIDUS -29FC; 29FD # LEFT-POINTING CURVED ANGLE BRACKET -29FD; 29FC # RIGHT-POINTING CURVED ANGLE BRACKET -2A2B; 2A2C # MINUS SIGN WITH FALLING DOTS -2A2C; 2A2B # MINUS SIGN WITH RISING DOTS -2A2D; 2A2E # PLUS SIGN IN LEFT HALF CIRCLE -2A2E; 2A2D # PLUS SIGN IN RIGHT HALF CIRCLE -2A34; 2A35 # MULTIPLICATION SIGN IN LEFT HALF CIRCLE -2A35; 2A34 # MULTIPLICATION SIGN IN RIGHT HALF CIRCLE -2A3C; 2A3D # INTERIOR PRODUCT -2A3D; 2A3C # RIGHTHAND INTERIOR PRODUCT -2A64; 2A65 # Z NOTATION DOMAIN ANTIRESTRICTION -2A65; 2A64 # Z NOTATION RANGE ANTIRESTRICTION -2A79; 2A7A # LESS-THAN WITH CIRCLE INSIDE -2A7A; 2A79 # GREATER-THAN WITH CIRCLE INSIDE -2A7D; 2A7E # LESS-THAN OR SLANTED EQUAL TO -2A7E; 2A7D # GREATER-THAN OR SLANTED EQUAL TO -2A7F; 2A80 # LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE -2A80; 2A7F # GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE -2A81; 2A82 # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE -2A82; 2A81 # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE -2A83; 2A84 # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT -2A84; 2A83 # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT -2A8B; 2A8C # LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN -2A8C; 2A8B # GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN -2A91; 2A92 # LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL -2A92; 2A91 # GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL -2A93; 2A94 # LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL -2A94; 2A93 # GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL -2A95; 2A96 # SLANTED EQUAL TO OR LESS-THAN -2A96; 2A95 # SLANTED EQUAL TO OR GREATER-THAN -2A97; 2A98 # SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE -2A98; 2A97 # SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE -2A99; 2A9A # DOUBLE-LINE EQUAL TO OR LESS-THAN -2A9A; 2A99 # DOUBLE-LINE EQUAL TO OR GREATER-THAN -2A9B; 2A9C # DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN -2A9C; 2A9B # DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN -2AA1; 2AA2 # DOUBLE NESTED LESS-THAN -2AA2; 2AA1 # DOUBLE NESTED GREATER-THAN -2AA6; 2AA7 # LESS-THAN CLOSED BY CURVE -2AA7; 2AA6 # GREATER-THAN CLOSED BY CURVE -2AA8; 2AA9 # LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL -2AA9; 2AA8 # GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL -2AAA; 2AAB # SMALLER THAN -2AAB; 2AAA # LARGER THAN -2AAC; 2AAD # SMALLER THAN OR EQUAL TO -2AAD; 2AAC # LARGER THAN OR EQUAL TO -2AAF; 2AB0 # PRECEDES ABOVE SINGLE-LINE EQUALS SIGN -2AB0; 2AAF # SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN -2AB3; 2AB4 # PRECEDES ABOVE EQUALS SIGN -2AB4; 2AB3 # SUCCEEDS ABOVE EQUALS SIGN -2ABB; 2ABC # DOUBLE PRECEDES -2ABC; 2ABB # DOUBLE SUCCEEDS -2ABD; 2ABE # SUBSET WITH DOT -2ABE; 2ABD # SUPERSET WITH DOT -2ABF; 2AC0 # SUBSET WITH PLUS SIGN BELOW -2AC0; 2ABF # SUPERSET WITH PLUS SIGN BELOW -2AC1; 2AC2 # SUBSET WITH MULTIPLICATION SIGN BELOW -2AC2; 2AC1 # SUPERSET WITH MULTIPLICATION SIGN BELOW -2AC3; 2AC4 # SUBSET OF OR EQUAL TO WITH DOT ABOVE -2AC4; 2AC3 # SUPERSET OF OR EQUAL TO WITH DOT ABOVE -2AC5; 2AC6 # SUBSET OF ABOVE EQUALS SIGN -2AC6; 2AC5 # SUPERSET OF ABOVE EQUALS SIGN -2ACD; 2ACE # SQUARE LEFT OPEN BOX OPERATOR -2ACE; 2ACD # SQUARE RIGHT OPEN BOX OPERATOR -2ACF; 2AD0 # CLOSED SUBSET -2AD0; 2ACF # CLOSED SUPERSET -2AD1; 2AD2 # CLOSED SUBSET OR EQUAL TO -2AD2; 2AD1 # CLOSED SUPERSET OR EQUAL TO -2AD3; 2AD4 # SUBSET ABOVE SUPERSET -2AD4; 2AD3 # SUPERSET ABOVE SUBSET -2AD5; 2AD6 # SUBSET ABOVE SUBSET -2AD6; 2AD5 # SUPERSET ABOVE SUPERSET -2ADE; 22A6 # SHORT LEFT TACK -2AE3; 22A9 # DOUBLE VERTICAL BAR LEFT TURNSTILE -2AE4; 22A8 # VERTICAL BAR DOUBLE LEFT TURNSTILE -2AE5; 22AB # DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE -2AEC; 2AED # DOUBLE STROKE NOT SIGN -2AED; 2AEC # REVERSED DOUBLE STROKE NOT SIGN -2AF7; 2AF8 # TRIPLE NESTED LESS-THAN -2AF8; 2AF7 # TRIPLE NESTED GREATER-THAN -2AF9; 2AFA # DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO -2AFA; 2AF9 # DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO -2E02; 2E03 # LEFT SUBSTITUTION BRACKET -2E03; 2E02 # RIGHT SUBSTITUTION BRACKET -2E04; 2E05 # LEFT DOTTED SUBSTITUTION BRACKET -2E05; 2E04 # RIGHT DOTTED SUBSTITUTION BRACKET -2E09; 2E0A # LEFT TRANSPOSITION BRACKET -2E0A; 2E09 # RIGHT TRANSPOSITION BRACKET -2E0C; 2E0D # LEFT RAISED OMISSION BRACKET -2E0D; 2E0C # RIGHT RAISED OMISSION BRACKET -2E1C; 2E1D # LEFT LOW PARAPHRASE BRACKET -2E1D; 2E1C # RIGHT LOW PARAPHRASE BRACKET -2E20; 2E21 # LEFT VERTICAL BAR WITH QUILL -2E21; 2E20 # RIGHT VERTICAL BAR WITH QUILL -2E22; 2E23 # TOP LEFT HALF BRACKET -2E23; 2E22 # TOP RIGHT HALF BRACKET -2E24; 2E25 # BOTTOM LEFT HALF BRACKET -2E25; 2E24 # BOTTOM RIGHT HALF BRACKET -2E26; 2E27 # LEFT SIDEWAYS U BRACKET -2E27; 2E26 # RIGHT SIDEWAYS U BRACKET -2E28; 2E29 # LEFT DOUBLE PARENTHESIS -2E29; 2E28 # RIGHT DOUBLE PARENTHESIS -3008; 3009 # LEFT ANGLE BRACKET -3009; 3008 # RIGHT ANGLE BRACKET -300A; 300B # LEFT DOUBLE ANGLE BRACKET -300B; 300A # RIGHT DOUBLE ANGLE BRACKET -300C; 300D # [BEST FIT] LEFT CORNER BRACKET -300D; 300C # [BEST FIT] RIGHT CORNER BRACKET -300E; 300F # [BEST FIT] LEFT WHITE CORNER BRACKET -300F; 300E # [BEST FIT] RIGHT WHITE CORNER BRACKET -3010; 3011 # LEFT BLACK LENTICULAR BRACKET -3011; 3010 # RIGHT BLACK LENTICULAR BRACKET -3014; 3015 # LEFT TORTOISE SHELL BRACKET -3015; 3014 # RIGHT TORTOISE SHELL BRACKET -3016; 3017 # LEFT WHITE LENTICULAR BRACKET -3017; 3016 # RIGHT WHITE LENTICULAR BRACKET -3018; 3019 # LEFT WHITE TORTOISE SHELL BRACKET -3019; 3018 # RIGHT WHITE TORTOISE SHELL BRACKET -301A; 301B # LEFT WHITE SQUARE BRACKET -301B; 301A # RIGHT WHITE SQUARE BRACKET -FE59; FE5A # SMALL LEFT PARENTHESIS -FE5A; FE59 # SMALL RIGHT PARENTHESIS -FE5B; FE5C # SMALL LEFT CURLY BRACKET -FE5C; FE5B # SMALL RIGHT CURLY BRACKET -FE5D; FE5E # SMALL LEFT TORTOISE SHELL BRACKET -FE5E; FE5D # SMALL RIGHT TORTOISE SHELL BRACKET -FE64; FE65 # SMALL LESS-THAN SIGN -FE65; FE64 # SMALL GREATER-THAN SIGN -FF08; FF09 # FULLWIDTH LEFT PARENTHESIS -FF09; FF08 # FULLWIDTH RIGHT PARENTHESIS -FF1C; FF1E # FULLWIDTH LESS-THAN SIGN -FF1E; FF1C # FULLWIDTH GREATER-THAN SIGN -FF3B; FF3D # FULLWIDTH LEFT SQUARE BRACKET -FF3D; FF3B # FULLWIDTH RIGHT SQUARE BRACKET -FF5B; FF5D # FULLWIDTH LEFT CURLY BRACKET -FF5D; FF5B # FULLWIDTH RIGHT CURLY BRACKET -FF5F; FF60 # FULLWIDTH LEFT WHITE PARENTHESIS -FF60; FF5F # FULLWIDTH RIGHT WHITE PARENTHESIS -FF62; FF63 # [BEST FIT] HALFWIDTH LEFT CORNER BRACKET -FF63; FF62 # [BEST FIT] HALFWIDTH RIGHT CORNER BRACKET - -# The following characters have no appropriate mirroring character. -# For these characters it is up to the rendering system -# to provide mirrored glyphs. - -# 2140; DOUBLE-STRUCK N-ARY SUMMATION -# 2201; COMPLEMENT -# 2202; PARTIAL DIFFERENTIAL -# 2203; THERE EXISTS -# 2204; THERE DOES NOT EXIST -# 2211; N-ARY SUMMATION -# 2216; SET MINUS -# 221A; SQUARE ROOT -# 221B; CUBE ROOT -# 221C; FOURTH ROOT -# 221D; PROPORTIONAL TO -# 221F; RIGHT ANGLE -# 2220; ANGLE -# 2221; MEASURED ANGLE -# 2222; SPHERICAL ANGLE -# 2224; DOES NOT DIVIDE -# 2226; NOT PARALLEL TO -# 222B; INTEGRAL -# 222C; DOUBLE INTEGRAL -# 222D; TRIPLE INTEGRAL -# 222E; CONTOUR INTEGRAL -# 222F; SURFACE INTEGRAL -# 2230; VOLUME INTEGRAL -# 2231; CLOCKWISE INTEGRAL -# 2232; CLOCKWISE CONTOUR INTEGRAL -# 2233; ANTICLOCKWISE CONTOUR INTEGRAL -# 2239; EXCESS -# 223B; HOMOTHETIC -# 223E; INVERTED LAZY S -# 223F; SINE WAVE -# 2240; WREATH PRODUCT -# 2241; NOT TILDE -# 2242; MINUS TILDE -# 2244; NOT ASYMPTOTICALLY EQUAL TO -# 2245; APPROXIMATELY EQUAL TO -# 2246; APPROXIMATELY BUT NOT ACTUALLY EQUAL TO -# 2247; NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO -# 2248; ALMOST EQUAL TO -# 2249; NOT ALMOST EQUAL TO -# 224A; ALMOST EQUAL OR EQUAL TO -# 224B; TRIPLE TILDE -# 224C; ALL EQUAL TO -# 225F; QUESTIONED EQUAL TO -# 2260; NOT EQUAL TO -# 2262; NOT IDENTICAL TO -# 228C; MULTISET -# 22A7; MODELS -# 22AA; TRIPLE VERTICAL BAR RIGHT TURNSTILE -# 22AC; DOES NOT PROVE -# 22AD; NOT TRUE -# 22AE; DOES NOT FORCE -# 22AF; NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE -# 22B8; MULTIMAP -# 22BE; RIGHT ANGLE WITH ARC -# 22BF; RIGHT TRIANGLE -# 22F5; ELEMENT OF WITH DOT ABOVE -# 22F8; ELEMENT OF WITH UNDERBAR -# 22F9; ELEMENT OF WITH TWO HORIZONTAL STROKES -# 22FF; Z NOTATION BAG MEMBERSHIP -# 2320; TOP HALF INTEGRAL -# 2321; BOTTOM HALF INTEGRAL -# 27C0; THREE DIMENSIONAL ANGLE -# 27CC; LONG DIVISION -# 27D3; LOWER RIGHT CORNER WITH DOT -# 27D4; UPPER LEFT CORNER WITH DOT -# 27DC; LEFT MULTIMAP -# 299B; MEASURED ANGLE OPENING LEFT -# 299C; RIGHT ANGLE VARIANT WITH SQUARE -# 299D; MEASURED RIGHT ANGLE WITH DOT -# 299E; ANGLE WITH S INSIDE -# 299F; ACUTE ANGLE -# 29A0; SPHERICAL ANGLE OPENING LEFT -# 29A1; SPHERICAL ANGLE OPENING UP -# 29A2; TURNED ANGLE -# 29A3; REVERSED ANGLE -# 29A4; ANGLE WITH UNDERBAR -# 29A5; REVERSED ANGLE WITH UNDERBAR -# 29A6; OBLIQUE ANGLE OPENING UP -# 29A7; OBLIQUE ANGLE OPENING DOWN -# 29A8; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT -# 29A9; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT -# 29AA; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT -# 29AB; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT -# 29AC; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP -# 29AD; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP -# 29AE; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN -# 29AF; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN -# 29C2; CIRCLE WITH SMALL CIRCLE TO THE RIGHT -# 29C3; CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT -# 29C9; TWO JOINED SQUARES -# 29CE; RIGHT TRIANGLE ABOVE LEFT TRIANGLE -# 29DC; INCOMPLETE INFINITY -# 29E1; INCREASES AS -# 29E3; EQUALS SIGN AND SLANTED PARALLEL -# 29E4; EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE -# 29E5; IDENTICAL TO AND SLANTED PARALLEL -# 29E8; DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK -# 29E9; DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK -# 29F4; RULE-DELAYED -# 29F6; SOLIDUS WITH OVERBAR -# 29F7; REVERSE SOLIDUS WITH HORIZONTAL STROKE -# 2A0A; MODULO TWO SUM -# 2A0B; SUMMATION WITH INTEGRAL -# 2A0C; QUADRUPLE INTEGRAL OPERATOR -# 2A0D; FINITE PART INTEGRAL -# 2A0E; INTEGRAL WITH DOUBLE STROKE -# 2A0F; INTEGRAL AVERAGE WITH SLASH -# 2A10; CIRCULATION FUNCTION -# 2A11; ANTICLOCKWISE INTEGRATION -# 2A12; LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE -# 2A13; LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE -# 2A14; LINE INTEGRATION NOT INCLUDING THE POLE -# 2A15; INTEGRAL AROUND A POINT OPERATOR -# 2A16; QUATERNION INTEGRAL OPERATOR -# 2A17; INTEGRAL WITH LEFTWARDS ARROW WITH HOOK -# 2A18; INTEGRAL WITH TIMES SIGN -# 2A19; INTEGRAL WITH INTERSECTION -# 2A1A; INTEGRAL WITH UNION -# 2A1B; INTEGRAL WITH OVERBAR -# 2A1C; INTEGRAL WITH UNDERBAR -# 2A1E; LARGE LEFT TRIANGLE OPERATOR -# 2A1F; Z NOTATION SCHEMA COMPOSITION -# 2A20; Z NOTATION SCHEMA PIPING -# 2A21; Z NOTATION SCHEMA PROJECTION -# 2A24; PLUS SIGN WITH TILDE ABOVE -# 2A26; PLUS SIGN WITH TILDE BELOW -# 2A29; MINUS SIGN WITH COMMA ABOVE -# 2A3E; Z NOTATION RELATIONAL COMPOSITION -# 2A57; SLOPING LARGE OR -# 2A58; SLOPING LARGE AND -# 2A6A; TILDE OPERATOR WITH DOT ABOVE -# 2A6B; TILDE OPERATOR WITH RISING DOTS -# 2A6C; SIMILAR MINUS SIMILAR -# 2A6D; CONGRUENT WITH DOT ABOVE -# 2A6F; ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT -# 2A70; APPROXIMATELY EQUAL OR EQUAL TO -# 2A73; EQUALS SIGN ABOVE TILDE OPERATOR -# 2A74; DOUBLE COLON EQUAL -# 2A7B; LESS-THAN WITH QUESTION MARK ABOVE -# 2A7C; GREATER-THAN WITH QUESTION MARK ABOVE -# 2A85; LESS-THAN OR APPROXIMATE -# 2A86; GREATER-THAN OR APPROXIMATE -# 2A87; LESS-THAN AND SINGLE-LINE NOT EQUAL TO -# 2A88; GREATER-THAN AND SINGLE-LINE NOT EQUAL TO -# 2A89; LESS-THAN AND NOT APPROXIMATE -# 2A8A; GREATER-THAN AND NOT APPROXIMATE -# 2A8D; LESS-THAN ABOVE SIMILAR OR EQUAL -# 2A8E; GREATER-THAN ABOVE SIMILAR OR EQUAL -# 2A8F; LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN -# 2A90; GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN -# 2A9D; SIMILAR OR LESS-THAN -# 2A9E; SIMILAR OR GREATER-THAN -# 2A9F; SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN -# 2AA0; SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN -# 2AA3; DOUBLE NESTED LESS-THAN WITH UNDERBAR -# 2AB1; PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO -# 2AB2; SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO -# 2AB5; PRECEDES ABOVE NOT EQUAL TO -# 2AB6; SUCCEEDS ABOVE NOT EQUAL TO -# 2AB7; PRECEDES ABOVE ALMOST EQUAL TO -# 2AB8; SUCCEEDS ABOVE ALMOST EQUAL TO -# 2AB9; PRECEDES ABOVE NOT ALMOST EQUAL TO -# 2ABA; SUCCEEDS ABOVE NOT ALMOST EQUAL TO -# 2AC7; SUBSET OF ABOVE TILDE OPERATOR -# 2AC8; SUPERSET OF ABOVE TILDE OPERATOR -# 2AC9; SUBSET OF ABOVE ALMOST EQUAL TO -# 2ACA; SUPERSET OF ABOVE ALMOST EQUAL TO -# 2ACB; SUBSET OF ABOVE NOT EQUAL TO -# 2ACC; SUPERSET OF ABOVE NOT EQUAL TO -# 2ADC; FORKING -# 2AE2; VERTICAL BAR TRIPLE RIGHT TURNSTILE -# 2AE6; LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL -# 2AEE; DOES NOT DIVIDE WITH REVERSED NEGATION SLASH -# 2AF3; PARALLEL WITH TILDE OPERATOR -# 2AFB; TRIPLE SOLIDUS BINARY RELATION -# 2AFD; DOUBLE SOLIDUS OPERATOR -# 1D6DB; MATHEMATICAL BOLD PARTIAL DIFFERENTIAL -# 1D715; MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL -# 1D74F; MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL -# 1D789; MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL -# 1D7C3; MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL - -# EOF diff --git a/BJC-Utils2/data/formats.sprop b/BJC-Utils2/data/formats.sprop deleted file mode 100644 index 72f6e74..0000000 --- a/BJC-Utils2/data/formats.sprop +++ /dev/null @@ -1,160 +0,0 @@ -# File storage for format strings - -################################################# -# Generic format strings for regular expressions. -################################################# - -## Format a regular expression for matching a delimiter separated list. -## Takes two parameters -## 1) The expression for each term -## 2) The expression for the delimiter -delimSeparatedList (?:%1$s(?:%2$s%1$s)*) - -###################################### -# CL format string regular expressions -###################################### - -## Format a regular expression for matching a potential CL format directive -## Has two parts -## 1) The optional set of prefix parameters -## 2) The optional modifier -## Captures three things -## 1) The prefix parameters -## 2) The modifiers -## 3) The directive name -## 4) The function name, if the directive was a function call. -clFormatDirective ~(?%1$s)?(?%2$s?)(?:%3$s) - -#################################################### -# Format strings for handling double-quoted strings. -#################################################### - -## Format the three types of string escapes into a valid pattern. -## The three types are: -## 1) Short escapes. -## 2) Octal escapes. -## 3) Unicode escapes. -stringEscape \\(%1$s|%2$s|%3$s) - -## Format the parts of a regex into one that matches java-style double-quoted strings. -## The parts are: -## 1) Anything that's not a possible escape sequence or quote. -## 2) A possible escape sequence. -doubleQuotes ("(%1$s|%2$s)*") - -##################################### -# Format strings for handling doubles -##################################### - -## Format a floating point exponent regex. -## The parts are: -## 1) Exponent indicator, -## 2) One or more digits. -fpExponent %1$s%2$s - -## Format a decimal number with an integer part. -## The parts are: -## 1) A series of decimal digits -## 2) An exponent. -## -## The number format is: -## 1) An integer part -## 2) An optional dot -## 3) An optional decimal part -## 4) An optional exponent -fpDecimalInteger (?:%1$s(?:\.?)(?:%1$s?)(?:%2$s)?) - -## Format a decimal number with no integer part. -## The parts are: -## 1) A series of decimal digits -## 2) An exponent. -## -## The number format is: -## 1) A dot -## 2) A decimal part -## 3) An optional exponent -fpDecimalDecimal (?:\.(?:%1$s)(?:%2$s)?) - -## Format a hexadecimal number with no decimal part. -## The parts are: -## 1) A series of hex digits -## -## The number format is: -## 1) A hex leader. -## 2) A series of hex digits. -## 3) An optional dot. -fpHexInteger (?:0[xX]%1$s(?:\.)?) - -## Format a hexadecimal number with a decimal part -## The parts are: -## 1) A series of hex digits. -## -## The number format is: -## 1) A hex leader. -## 2) A optional series of hex digits. -## 3) A dot. -## 4) A series of hex digits. -fpHexDecimal (?:0[xX]%1$s?(?:\.)%1$s) - -## Format a hexadecimal leader before a prefix. -## The parts are: -## 1) A hex number with no decimal part -## 2) A hex number with a decimal part -fpHexLeader (?:%1$s|%2$s) - -## Format a hexadecimal floating point number. -## The parts are: -## 1) A hexadecimal leader. -## 2) A series of decimal digits. -## -## The number format is: -## 1) A hexadecimal leader. -## 2) A exponent indicator. -## 3) An optional sign. -## 4) A series of decimal digits. -fpHexString (?:%1$s[pP][+-]?%2$s) - -## Format the number part of a double. -## The parts are: -## 1) A decimal double with an integer part. -## 2) A decimal double without an integer part. -## 3) A hexadecimal double. -fpNumber (?:%1$s|%2$s|%3$s) - -## Format a floating point leader. -## -## NOTE: The other parts are completed by where we're inserted. - -## Format a double -## The parts are: -## 1) A leader -## 2) A number -## -## NOTE: The parens are not mismatched. -## The other one is contributed by the leader. -fpDouble %1$s(?:%2$s[fFdD]?))[\x00-\x20]* - -######################################### -# Format strings for handling delimiters. -######################################### - -## Format a raw delimiter -## The parts are -## 1) A regular expression -## -## This matches just the provided regular expression. -rawDelim (?:%1$s)| - -## Format a repeating delimiter -## The parts are -## 1) A string. -## -## This matches one or more occurances of the provided string as a literal. -multipleDelim (?:\Q%1$s\E)+| - -## Format a simple delimiter -## The parts are -## 1) A string. -## -## This matches one occurrence of the provided string as a literal. -simpleDelim (?:\Q%1$s\E)| diff --git a/BJC-Utils2/data/regexes.sprop b/BJC-Utils2/data/regexes.sprop deleted file mode 100644 index 89c5b4f..0000000 --- a/BJC-Utils2/data/regexes.sprop +++ /dev/null @@ -1,61 +0,0 @@ -# File storage for static regular expressions. - -######################################################## -# Regular expressions for handling double-quoted strings -######################################################## - -## Match a possible single character escape -possibleStringEscape \\. - -## Match valid string escapes -shortFormStringEscape [btnfr"'\\] -octalStringEscape [0-3]?[0-7]{1,2} -unicodeStringEscape u[0-9a-fA-F]{4} - -## Match an unescaped quote in a string. -unescapedQuote (?[\S&&[^/]])|(?:/(?[\S&&[^/]]+)/)) -############################################## -# Miscellaneous validation regular expressions -############################################## -intLiteral \A[+\-]\d+\Z \ No newline at end of file diff --git a/BJC-Utils2/docs/man5/BlockReaderCLI.5 b/BJC-Utils2/docs/man5/BlockReaderCLI.5 deleted file mode 100644 index 178b6de..0000000 --- a/BJC-Utils2/docs/man5/BlockReaderCLI.5 +++ /dev/null @@ -1,8 +0,0 @@ -.TH BlockReaderCLI 5 "2017-09-10" "" "" -.SH NAME -BlockReaderCLI \- configure \fBBlockReaders\fP from a script -.SH DESCRIPTION -A small language for configuring \fBBlockReaders\fP from a script. This is a -command based language, where each command comes on its own line for -configuration. Certain commands may start a sub-mode. -.SH diff --git a/BJC-Utils2/pom.xml b/BJC-Utils2/pom.xml deleted file mode 100644 index 78c251d..0000000 --- a/BJC-Utils2/pom.xml +++ /dev/null @@ -1,80 +0,0 @@ - - 4.0.0 - - - - - maven-compiler-plugin - - 1.8 - 1.8 - - - - - - data/ - - **/*.txt - **/*.sprop - - - - - - bjc - BJC-Utils2 - 0.1.0-SNAPSHOT - jar - - BJC-Utils2 - http://maven.apache.org - - - UTF-8 - - - - - junit - junit - 4.12 - - - org.junit.contrib - junit-theories - 4.12 - - - com.pholser - junit-quickcheck-core - 0.5 - - - com.pholser - junit-quickcheck-generators - 0.5 - - - org.apache.commons - commons-lang3 - 3.4 - - - org.json - json - 20160212 - - - com.google.guava - guava - 21.0 - - - com.ibm.icu - icu4j - 58.2 - - - diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/AbbrevMapTest.java b/BJC-Utils2/src/examples/java/bjc/utils/examples/AbbrevMapTest.java deleted file mode 100644 index ac4ea76..0000000 --- a/BJC-Utils2/src/examples/java/bjc/utils/examples/AbbrevMapTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package bjc.utils.examples; - -import java.util.Scanner; - -import bjc.utils.esodata.AbbrevMap; -import bjc.utils.funcutils.StringUtils; - -/** - * Test for abbreviation map. - * - * @author EVE - * - */ -public class AbbrevMapTest { - /** - * Main method. - * - * @param args - * Unused CLI args. - */ - public static void main(final String[] args) { - final Scanner scn = new Scanner(System.in); - - final AbbrevMap map = new AbbrevMap(); - - System.out.print("Enter a command (blank line to quit): "); - String ln = scn.nextLine(); - - while (!ln.equals("")) { - final String[] commParts = ln.split(" "); - - switch (commParts[0]) { - case "add": - map.addWords(commParts[1]); - break; - case "remove": - map.removeWords(commParts[1]); - break; - case "recalc": - map.recalculate(); - break; - case "check": - final String list = StringUtils.toEnglishList(map.deabbrev(commParts[1]), false); - System.out.println(list); - break; - case "debug": - System.out.println(map.toString()); - break; - default: - System.out.println("Unknown command: " + ln); - } - - System.out.print("Enter a command (blank line to quit): "); - ln = scn.nextLine(); - } - - scn.close(); - } -} diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/BinarySearchTest.java b/BJC-Utils2/src/examples/java/bjc/utils/examples/BinarySearchTest.java deleted file mode 100644 index 758af61..0000000 --- a/BJC-Utils2/src/examples/java/bjc/utils/examples/BinarySearchTest.java +++ /dev/null @@ -1,123 +0,0 @@ -package bjc.utils.examples; - -import java.util.Scanner; - -import bjc.utils.funcdata.bst.BinarySearchTree; -import bjc.utils.funcdata.bst.TreeLinearizationMethod; - -/** - * Example showing how to use the binary search tree. - * - * @author ben - * - */ -public class BinarySearchTest { - private static void display(final BinarySearchTree tree, final Scanner input) { - System.out.print("What order would you like the tree to be printed in (m for options): "); - char command; - - while (true) { - command = input.nextLine().charAt(0); - TreeLinearizationMethod method = null; - - switch (command) { - case 'm': - System.out.println("Possible tree printing methods: "); - System.out.println("\tp: Preorder printing (print parent first, then left & right)."); - System.out.println("\ti: Inorder printing (print left first, then parent & right)."); - System.out.println("\to: Postorder printing (print left first, then right & parent)."); - break; - case 'p': - method = TreeLinearizationMethod.PREORDER; - break; - case 'i': - method = TreeLinearizationMethod.INORDER; - break; - case 'o': - method = TreeLinearizationMethod.POSTORDER; - break; - default: - System.out.println("ERROR: Unknown command."); - } - - if (method != null) { - tree.traverse(method, (element) -> { - System.out.println("Node: " + element); - return true; - }); - - return; - } - - System.out.print("What order would you like the tree to be printed in (m for options): "); - } - } - - /** - * Main method of class - * - * @param args - * Unused CLI args - */ - public static void main(final String[] args) { - final Scanner input = new Scanner(System.in); - System.out.println("Binary Tree Constructor/Searcher"); - final BinarySearchTree tree = new BinarySearchTree<>((o1, o2) -> o1 - o2); - - char command = ' '; - while (command != 'e') { - System.out.print("Enter a command (m for help): "); - command = input.nextLine().charAt(0); - - switch (command) { - case 'm': - System.out.println("Valid commands: "); - System.out.println("\tm: Display this help message."); - System.out.println("\te: Exit this program."); - System.out.println("\ta: Add a node to the binary tree."); - System.out.println("\td: Display the binary tree."); - System.out.println("\tr: Remove a node from the binary tree."); - System.out.println("\tf: Check if a given node is in the binary tree."); - System.out.println("\tt: Trim all deleted nodes from the tree."); - System.out.println("\tb: Balance the tree (also trims dead nodes)"); - break; - case 'a': - System.out.print("Enter the letter to add to the binary tree: "); - command = input.nextLine().charAt(0); - - tree.addNode(command); - break; - case 'r': - System.out.print("Enter the letter to add to the binary tree: "); - command = input.nextLine().charAt(0); - - tree.deleteNode(command); - break; - case 'd': - display(tree, input); - break; - case 'f': - System.out.print("Enter the letter to add to the binary tree: "); - command = input.nextLine().charAt(0); - - final boolean inTree = tree.isInTree(command); - if (inTree) { - System.out.printf("Node %s was found\n", command); - } else { - System.out.printf("Node %s was not found\n", command); - } - break; - case 't': - tree.trim(); - break; - case 'b': - tree.balance(); - break; - default: - System.out.println("ERROR: Unrecognized command."); - } - } - - input.close(); - } -} diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/DelimSplitterTest.java b/BJC-Utils2/src/examples/java/bjc/utils/examples/DelimSplitterTest.java deleted file mode 100644 index 428f276..0000000 --- a/BJC-Utils2/src/examples/java/bjc/utils/examples/DelimSplitterTest.java +++ /dev/null @@ -1,504 +0,0 @@ -package bjc.utils.examples; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Scanner; - -import bjc.utils.data.ITree; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; -import bjc.utils.funcutils.StringUtils; -import bjc.utils.parserutils.delims.DelimiterException; -import bjc.utils.parserutils.delims.DelimiterGroup; -import bjc.utils.parserutils.delims.RegexCloser; -import bjc.utils.parserutils.delims.RegexOpener; -import bjc.utils.parserutils.delims.SequenceDelimiter; -import bjc.utils.parserutils.delims.StringDelimiter; -import bjc.utils.parserutils.splitter.ConfigurableTokenSplitter; - -/** - * Test for {@link SequenceDelimiter} as well as - * {@link ConfigurableTokenSplitter} - * - * @author EVE - * - */ -public class DelimSplitterTest { - private ConfigurableTokenSplitter split; - - private StringDelimiter dlm; - - private Map mirrored; - - private Map> groups; - - boolean verbose; - - /* - * Create a new tester. - */ - private DelimSplitterTest() { - loadMirrorDB(); - - groups = new HashMap<>(); - - split = new ConfigurableTokenSplitter(true); - - dlm = new StringDelimiter(); - - verbose = true; - } - - private void loadMirrorDB() { - mirrored = new HashMap<>(); - - final InputStream stream = getClass().getResourceAsStream("/BidiMirrorDB.txt"); - - try (Scanner scn = new Scanner(stream)) { - String ln = ""; - - while (scn.hasNextLine()) { - ln = scn.nextLine(); - - if (ln.equals("")) { - continue; - } - if (ln.startsWith("#")) { - continue; - } - - final int cp1 = Integer.parseInt(ln.substring(0, 4), 16); - final int cp2 = Integer.parseInt(ln.substring(6, 10), 16); - - final char[] cpa1 = Character.toChars(cp1); - final char[] cpa2 = Character.toChars(cp2); - - final String cps1 = new String(cpa1); - final String cps2 = new String(cpa2); - - mirrored.put(cps1, cps2); - } - } - } - - /* - * Run the tester interface. - */ - private void runLoop() { - final Scanner scn = new Scanner(System.in); - - System.out.print("Enter a command (blank line to quit): "); - String inp = scn.nextLine().trim(); - System.out.println(); - - while (!inp.equals("")) { - handleCommand(inp, scn, true); - - System.out.println(); - - System.out.print("Enter a command (blank line to quit): "); - inp = scn.nextLine(); - - System.out.println(); - } - - scn.close(); - } - - /* - * Handle a input command. - */ - private void handleCommand(final String inp, final Scanner scn, final boolean isInteractive) { - if (inp.equals("")) return; - - int idx = inp.indexOf(' '); - - if (idx == -1) { - idx = inp.length(); - } - - final String command = inp.substring(0, idx); - - final String args = inp.substring(idx).trim(); - final String[] argArray = args.split(" "); - - switch (command) { - case "test": - handleTest(args, false); - break; - case "test-ws": - handleTest(args, true); - break; - case "splitter-split": - handleSplit(argArray); - break; - case "splitter-compile": - split.compile(); - if (verbose) { - System.out.println("Compiled splitter"); - } - break; - case "splitter-add": - split.addSimpleDelimiters(argArray); - if (verbose) { - System.out.println("Added delimiters " + StringUtils.toEnglishList(argArray, true)); - } - break; - case "splitter-addmulti": - split.addMultiDelimiters(argArray); - if (verbose) { - System.out.println( - "Added multi-delimiters " + StringUtils.toEnglishList(argArray, true)); - } - break; - case "splitter-addmatch": - for (final String arg : argArray) { - split.addSimpleDelimiters(arg, mirrored.get(arg)); - } - if (verbose) { - System.out.println("Added matched delimiters " - + StringUtils.toEnglishList(argArray, true)); - } - break; - case "splitter-debug": - System.out.println(split.toString()); - break; - case "splitter-reset": - split = new ConfigurableTokenSplitter(true); - if (verbose) { - System.out.println("Reset splitter"); - } - break; - - case "delims-addgroup": - for (final String arg : argArray) { - dlm.addGroup(groups.get(arg)); - } - if (verbose) { - System.out.println("Added groups " + StringUtils.toEnglishList(argArray, true)); - } - break; - case "delims-setinitial": - dlm.setInitialGroup(groups.get(argArray[0])); - if (verbose) { - System.out.println("Set initial group"); - } - break; - case "delims-debug": - System.out.println(dlm.toString()); - break; - case "delims-test": - handleDelim(args); - break; - case "delims-reset": - dlm = new StringDelimiter(); - if (verbose) { - System.out.println("Reset delimiter"); - } - break; - case "delimgroups-new": - for (final String arg : argArray) { - groups.put(arg, new DelimiterGroup<>(arg)); - } - if (verbose) { - System.out.println("Created groups " + StringUtils.toEnglishList(argArray, true)); - } - break; - case "delimgroups-edit": - for (final String arg : argArray) { - handleEditGroup(arg, scn, isInteractive); - } - break; - case "delimgroups-debug": - for (final DelimiterGroup group : groups.values()) { - System.out.println(group.toString()); - } - break; - case "delimgroups-reset": - dlm = new StringDelimiter(); - groups = new HashMap<>(); - if (verbose) { - System.out.println("Reset delimiter groups + delimiter"); - } - break; - case "load-file": - handleLoadFile(args); - break; - default: - System.out.println("Unknown command "); - } - - } - - /* - * Load script commands from a file. - */ - private void handleLoadFile(final String args) { - String pth = args; - - if (args.startsWith("\"")) { - pth = args.substring(1, args.length() - 1); - } - - try (FileInputStream fis = new FileInputStream(pth)) { - final Scanner scn = new Scanner(fis); - - while (scn.hasNextLine()) { - final String ln = scn.nextLine().trim(); - - if (ln.equals("")) { - continue; - } - if (ln.startsWith("#")) { - continue; - } - - if (verbose) { - System.out.println("\nRead command '" + ln + "' from file\n"); - } - handleCommand(ln, scn, false); - } - - scn.close(); - } catch (final FileNotFoundException fnfex) { - System.out.println("Couldn't find file '" + args + "'"); - } catch (final IOException ioex) { - System.out.println("I/O error with file '" + args + "'\nCause: " + ioex.getMessage()); - } - } - - /* - * Handle editing a group. - */ - private void handleEditGroup(final String arg, final Scanner scn, final boolean isInteractive) { - if (!groups.containsKey(arg)) { - System.out.println("No group named '" + arg + "'"); - return; - } - - final DelimiterGroup group = groups.get(arg); - - if (verbose) { - System.out.println("Editing group '" + arg + "'"); - } - if (isInteractive) { - System.out.println("Enter command (blank line to stop editing): "); - } - - String ln = scn.nextLine().trim(); - - while (!ln.equals("")) { - int idx = ln.indexOf(' '); - - if (idx == -1) { - idx = ln.length(); - } - - final String command = ln.substring(0, idx); - - final String args = ln.substring(idx).trim(); - final String[] argArray = args.split(" "); - - switch (command) { - case "add-closing": - group.addClosing(argArray); - if (verbose) { - System.out.println( - "Added closers " + StringUtils.toEnglishList(argArray, true)); - } - break; - case "add-tlexclude": - group.addTopLevelForbid(argArray); - if (verbose) { - System.out.println("Added top-level exclusions " - + StringUtils.toEnglishList(argArray, true)); - } - break; - case "add-exclude": - group.addTopLevelForbid(argArray); - if (verbose) { - System.out.println("Added nested exclusions " - + StringUtils.toEnglishList(argArray, true)); - } - break; - case "add-subgroup": - group.addSubgroup(argArray[0], Integer.parseInt(argArray[1])); - if (verbose) { - System.out.printf("Added subgroup %s with priority %s\n", argArray[0], - argArray[1]); - } - break; - case "add-implied-subgroup": - group.implySubgroup(argArray[0], argArray[1]); - if (verbose) { - System.out.printf("Made closer '%s' imply a '%s' subgroup\n", argArray[0], - argArray[1]); - } - break; - case "add-opener": - group.addOpener(argArray[0], argArray[1]); - if (verbose) { - System.out.printf("Added opener '%s' for group '%s'\n", argArray[0], - argArray[1]); - } - break; - case "add-reopener": - group.addPredOpener(new RegexOpener(argArray[0], argArray[1])); - if (verbose) { - System.out.printf("Added regex '%s' as opener for '%s'\n", argArray[1], - argArray[0]); - } - break; - case "add-recloser": - group.addPredCloser(new RegexCloser(argArray[0])); - if (verbose) { - System.out.printf("Added parameterized string '%s' as closer\n", argArray[0]); - } - break; - case "debug": - System.out.println(group.toString()); - break; - default: - System.out.println("Unknown command " + command); - } - - if (isInteractive) { - System.out.println("Enter command (blank line to stop editing): "); - } - - ln = scn.nextLine().trim(); - } - - if (verbose) { - System.out.println("Finished editing group '" + arg + "'"); - } - } - - private void handleDelim(final String args) { - try { - final ITree res = dlm.delimitSequence(args.split(" ")); - - printDelimSeq(res); - } catch (final DelimiterException dex) { - System.out.println("Expression '" + args + "' isn't properly delimited.\n\tCause: " - + dex.getMessage()); - } - } - - private void handleSplit(final String[] argArray) { - for (int i = 0; i < argArray.length; i++) { - final String arg = argArray[i]; - - final IList strangs = split.split(arg); - - System.out.printf("%d '%s' %s\n", i, arg, strangs); - } - } - - private void handleTest(final String inp, final boolean splitWS) { - IList strings; - - try { - strings = split.split(inp); - } catch (final IllegalStateException isex) { - System.out.println("Splitter must be compiled at least once before use."); - return; - } - - System.out.println("Split tokens: " + strings); - - if (splitWS) { - final List tks = new LinkedList<>(); - - for (final String strang : strings) { - tks.addAll(Arrays.asList(strang.split(" "))); - } - - strings = new FunctionalList<>(tks); - } - try { - final ITree delim = dlm.delimitSequence(strings.toArray(new String[0])); - - printDelimSeq(delim); - } catch (final DelimiterException dex) { - System.out.println("Expression isn't properly delimited."); - System.out.println("Cause: " + dex.getMessage()); - } - } - - private void printDelimSeq(final ITree delim) { - System.out.println("Delimited tokens:\n" + delim.getChild(1).toString()); - System.out.print("Delimited expr: "); - printDelimTree(delim); - System.out.println(); - System.out.println(); - - System.out.println(); - } - - private void printDelimTree(final ITree tree) { - final StringBuilder sb = new StringBuilder(); - - intPrintDelimTree(tree.getChild(1), sb); - - System.out.println(sb.toString().replaceAll("\\s+", " ")); - } - - private void intPrintDelimTree(final ITree tree, final StringBuilder sb) { - tree.doForChildren((child) -> { - intPrintDelimNode(child, sb); - }); - } - - private void intPrintDelimNode(final ITree tree, final StringBuilder sb) { - if (tree.getHead().equals("contents")) { - intPrintDelimTree(tree, sb); - return; - } - - switch (tree.getChildrenCount()) { - case 0: - sb.append(tree.getHead()); - sb.append(" "); - - break; - case 1: - intPrintDelimTree(tree.getChild(0), sb); - - break; - case 2: - intPrintDelimTree(tree.getChild(0).getChild(0), sb); - intPrintDelimNode(tree.getChild(1), sb); - - break; - case 3: - intPrintDelimNode(tree.getChild(0), sb); - - final ITree contents = tree.getChild(1); - - intPrintDelimTree(contents.getChild(0), sb); - intPrintDelimNode(tree.getChild(2), sb); - - break; - } - } - - /** - * Main method - * - * @param args - * Unused CLI args. - */ - public static void main(final String[] args) { - final DelimSplitterTest tst = new DelimSplitterTest(); - - tst.runLoop(); - } -} diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/ShuntTest.java b/BJC-Utils2/src/examples/java/bjc/utils/examples/ShuntTest.java deleted file mode 100644 index ed530ed..0000000 --- a/BJC-Utils2/src/examples/java/bjc/utils/examples/ShuntTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package bjc.utils.examples; - -import java.util.Scanner; - -import bjc.utils.funcdata.FunctionalStringTokenizer; -import bjc.utils.funcdata.IList; -import bjc.utils.parserutils.ShuntingYard; - -/** - * Test of shunting yard - * - * @author ben - * - */ -public class ShuntTest { - /** - * Main method - * - * @param args - * Unused CLI args - */ - public static void main(final String[] args) { - final Scanner inputSource = new Scanner(System.in); - - System.out.print("Enter a expression to shunt: "); - final String line = inputSource.nextLine(); - - final ShuntingYard yard = new ShuntingYard<>(true); - - final IList preTokens = new FunctionalStringTokenizer(line).toList(strang -> strang); - final IList shuntedTokens = yard.postfix(preTokens, strang -> strang); - - System.out.println(shuntedTokens.toString()); - - inputSource.close(); - } -} diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/rangen/DiabloItemGen.java b/BJC-Utils2/src/examples/java/bjc/utils/examples/rangen/DiabloItemGen.java deleted file mode 100644 index 250318f..0000000 --- a/BJC-Utils2/src/examples/java/bjc/utils/examples/rangen/DiabloItemGen.java +++ /dev/null @@ -1,94 +0,0 @@ -package bjc.utils.examples.rangen; - -import bjc.utils.funcdata.FunctionalStringTokenizer; -import bjc.utils.funcdata.IList; -import bjc.utils.gen.WeightedGrammar; - -/** - * Example showing how to use the weighted random number generator. - * - * @author ben - * - */ -public class DiabloItemGen { - private static WeightedGrammar rules = new WeightedGrammar<>(); - - private static void addCase(final String ruleName, final int probability, final String ruleParts) { - final IList parts = FunctionalStringTokenizer.fromString(ruleParts).toList(strang -> strang); - - rules.addCase(ruleName, probability, parts); - } - - private static void addInfixRules() { - final String rn = ""; - - addCase(rn, 60, "sword"); - addCase(rn, 50, "armor"); - addCase(rn, 40, "rune"); - addCase(rn, 30, "scroll"); - addCase(rn, 20, "potion"); - addCase(rn, 10, "helm"); - } - - private static void addItemRules() { - final String rn = ""; - - addCase(rn, 10, ""); - addCase(rn, 20, " "); - addCase(rn, 30, " "); - addCase(rn, 40, " "); - addCase(rn, 50, " "); - addCase(rn, 60, " "); - } - - private static void addPrefixRules() { - final String rn = ""; - - addCase(rn, 60, "sturdy"); - addCase(rn, 50, "fine"); - addCase(rn, 40, "strong"); - addCase(rn, 30, "azure"); - addCase(rn, 20, "crimson"); - addCase(rn, 10, "phasing"); - } - - private static void addSuffixRules() { - final String rn = ""; - - addCase(rn, 60, "of Health"); - addCase(rn, 50, "of Wealth"); - addCase(rn, 40, "of Life"); - addCase(rn, 30, "of the Jackal"); - addCase(rn, 20, "of Vitality"); - addCase(rn, 10, "of Ability"); - } - - /** - * Main Method - * - * @param args - * Unused CLI args - */ - public static void main(final String[] args) { - rules.addRule(""); - addItemRules(); - - rules.addRule(""); - addSuffixRules(); - - rules.addRule(""); - addPrefixRules(); - - rules.addRule(""); - addInfixRules(); - - for (int i = 0; i < 100; i++) { - final IList ls = rules.generateListValues("", " "); - - final StringBuilder sb = new StringBuilder(); - ls.forEach(sb::append); - - System.out.println(sb.toString().replaceAll("\\s+", " ")); - } - } -} diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/rangen/RandomStringExamples.java b/BJC-Utils2/src/examples/java/bjc/utils/examples/rangen/RandomStringExamples.java deleted file mode 100644 index a84f70d..0000000 --- a/BJC-Utils2/src/examples/java/bjc/utils/examples/rangen/RandomStringExamples.java +++ /dev/null @@ -1,64 +0,0 @@ -package bjc.utils.examples.rangen; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.FunctionalStringTokenizer; -import bjc.utils.funcdata.IList; -import bjc.utils.gen.RandomGrammar; - -/** - * Examples of random grammar - * - * @author ben - * - */ -public class RandomStringExamples { - private static RandomGrammar rg; - - private static void addRule(final String rule, final String... cases) { - final IList> cses = new FunctionalList<>(); - - for (final String strang : cases) { - final IList lst = FunctionalStringTokenizer.fromString(strang).toList(s -> s); - - cses.add(lst); - } - - rg.makeRule(rule, cses); - } - - /** - * Main method - * - * @param args - * Unused CLI args - */ - public static void main(final String[] args) { - rg = new RandomGrammar<>(); - - addRule("", " ", " thinks that I am ", - "I ", "You think that I am "); - - addRule("", "dancing", "eating", "sleeping"); - - addRule("", "", "life", "my computer", "my friends"); - - addRule("", "hate", "am jealous of", "love"); - - addRule("", "hates", "loves"); - - addRule("", "my sister", "my father", "my girlfriend", "the man next door"); - - addRule("", "creative", "intelligent"); - - addRule("", "", " with ", ""); - - for (int i = 0; i < 10; i++) { - final IList ls = rg.generateListValues("", " "); - - final StringBuilder sb = new StringBuilder(); - ls.forEach(sb::append); - - System.out.println(sb.toString().replaceAll("\\s+", " ")); - } - } -} diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/sample-ds-files/html.ds b/BJC-Utils2/src/examples/java/bjc/utils/examples/sample-ds-files/html.ds deleted file mode 100644 index 103fa12..0000000 --- a/BJC-Utils2/src/examples/java/bjc/utils/examples/sample-ds-files/html.ds +++ /dev/null @@ -1,10 +0,0 @@ -delimgroups-new tag initial - -delimgroups-edit tag - add-recloser - -delimgroups-edit initial - add-reopener tag <(\w+)> - -delims-addgroup tag initial -delims-setinitial initial \ No newline at end of file diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/sample-ds-files/json.ds b/BJC-Utils2/src/examples/java/bjc/utils/examples/sample-ds-files/json.ds deleted file mode 100644 index d110d95..0000000 --- a/BJC-Utils2/src/examples/java/bjc/utils/examples/sample-ds-files/json.ds +++ /dev/null @@ -1,23 +0,0 @@ -splitter-addmatch ( { [ -splitter-add : , " -splitter-compile - -delimgroups-new braces brackets initial -delimgroups-edit braces - add-closing } - add-subgroup : 0 - add-subgroup , 1 - add-implied-subgroup } , - -delimgroups-edit brackets - add-subgroup , 0 - add-closing ] - add-implied-subgroup ] , - -delimgroups-edit initial - add-opener { braces - add-opener [ brackets - -delims-addgroup braces brackets initial - -delims-setinitial initial \ No newline at end of file diff --git a/BJC-Utils2/src/examples/java/bjc/utils/examples/test.tree b/BJC-Utils2/src/examples/java/bjc/utils/examples/test.tree deleted file mode 100644 index 795cc88..0000000 --- a/BJC-Utils2/src/examples/java/bjc/utils/examples/test.tree +++ /dev/null @@ -1,13 +0,0 @@ -test 1 - 1 - 1 - 2 - 2 - 1 - -simp 1 - 2 - 3 - 4 - 3 - 2 \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/PropertyDB.java b/BJC-Utils2/src/main/java/bjc/utils/PropertyDB.java deleted file mode 100644 index 713e1e0..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/PropertyDB.java +++ /dev/null @@ -1,160 +0,0 @@ -package bjc.utils; - -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.regex.Pattern; - -import bjc.utils.funcutils.LambdaLock; -import bjc.utils.ioutils.SimpleProperties; - -/** - * Database for storage of properties from external files. - * - * @author EVE - * - */ -public class PropertyDB { - private static SimpleProperties regexes; - private static Map compiledRegexes; - - private static SimpleProperties formats; - - /* - * Whether or not to log during the loading. - */ - private static final boolean LOGLOAD = false; - - /* - * The lock to use to ensure a read can't happen during a reload - */ - private static LambdaLock loadLock = new LambdaLock(); - - static { - reloadProperties(); - } - - /** - * Reload all the properties from their files. - * - * NOTE: Any attempts to read from the property DB while properties are - * being loaded will block, to prevent reads from partial states. - */ - public static void reloadProperties() { - /* - * Do the load with the write lock taken. - */ - loadLock.write(() -> { - if (LOGLOAD) { - System.out.println("Reading regex properties:"); - } - - /* - * Load regexes. - */ - regexes = new SimpleProperties(); - regexes.loadFrom(PropertyDB.class.getResourceAsStream("/regexes.sprop"), false); - if (LOGLOAD) { - regexes.outputProperties(); - System.out.println(); - } - compiledRegexes = new HashMap<>(); - - if (LOGLOAD) { - System.out.println("Reading format properties:"); - } - - /* - * Load formats. - */ - formats = new SimpleProperties(); - formats.loadFrom(PropertyDB.class.getResourceAsStream("/formats.sprop"), false); - if (LOGLOAD) { - formats.outputProperties(); - System.out.println(); - } - }); - } - - /** - * Retrieve a persisted regular expression. - * - * @param key - * The name of the regular expression. - * - * @return The regular expression with that name. - */ - public static String getRegex(final String key) { - return loadLock.read(() -> { - if (!regexes.containsKey(key)) { - final String msg = String.format("No regular expression named '%s' found", key); - - throw new NoSuchElementException(msg); - } - - return regexes.get(key); - }); - } - - /** - * Retrieve a persisted regular expression, compiled into a regular - * expression. - * - * @param key - * The name of the regular expression. - * - * @return The regular expression with that name. - */ - public static Pattern getCompiledRegex(final String key) { - return loadLock.read(() -> { - if (!regexes.containsKey(key)) { - final String msg = String.format("No regular expression named '%s' found", key); - - throw new NoSuchElementException(msg); - } - - /* - * Get the regex, and cache a compiled version. - */ - return compiledRegexes.computeIfAbsent(key, strang -> { - return Pattern.compile(regexes.get(strang)); - }); - }); - } - - /** - * Retrieve a persisted format string. - * - * @param key - * The name of the format string. - * - * @return The format string with that name. - */ - public static String getFormat(final String key) { - return loadLock.read(() -> { - if (!formats.containsKey(key)) { - final String msg = String.format("No format string named '%s' found", key); - - throw new NoSuchElementException(msg); - } - - return formats.get(key); - }); - } - - /** - * Retrieve a persisted format string, and apply it to a set of - * arguments. - * - * @param key - * The name of the format string. - * - * @param objects - * The parameters to the format string. - * - * @return The format string with that name. - */ - public static String applyFormat(final String key, final Object... objects) { - return String.format(getFormat(key), objects); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/CLICommander.java b/BJC-Utils2/src/main/java/bjc/utils/cli/CLICommander.java deleted file mode 100644 index cccb255..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/CLICommander.java +++ /dev/null @@ -1,134 +0,0 @@ -package bjc.utils.cli; - -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.Arrays; -import java.util.Scanner; - -/** - * Runs a CLI interface from the provided set of streams. - * - * @author ben - * - */ -public class CLICommander { - /* - * The streams used for input and normal/error output. - */ - private final InputStream input; - private final OutputStream output; - private final OutputStream error; - - /* - * The command mode to start execution in. - */ - private CommandMode initialMode; - - /** - * Create a new CLI interface powered by streams. - * - * @param input - * The stream to get user input from. - * @param output - * The stream to send normal output to. - * @param error - * The stream to send error output to. - */ - public CLICommander(final InputStream input, final OutputStream output, final OutputStream error) { - if (input == null) - throw new NullPointerException("Input stream must not be null"); - else if (output == null) - throw new NullPointerException("Output stream must not be null"); - else if (error == null) throw new NullPointerException("Error stream must not be null"); - - this.input = input; - this.output = output; - this.error = error; - } - - /** - * Start handling commands from the given input stream. - */ - public void runCommands() { - /* - * Setup output streams. - */ - final PrintStream normalOutput = new PrintStream(output); - final PrintStream errorOutput = new PrintStream(error); - - /* - * Set up input streams. - * - * We're suppressing the warning because we might use the input - * stream multiple times. - */ - @SuppressWarnings("resource") - final Scanner inputSource = new Scanner(input); - - /* - * The mode currently being used to handle commands. - * - * Used to preserve the initial mode. - */ - CommandMode currentMode = initialMode; - - /* - * Process commands until we're told to stop. - */ - while (currentMode != null) { - /* - * Print out the command prompt, using a custom prompt - * if one is specified. - */ - if (currentMode.isCustomPromptEnabled()) { - normalOutput.print(currentMode.getCustomPrompt()); - } else { - normalOutput.print(currentMode.getName() + ">> "); - } - - /* - * Read in a command. - */ - final String currentLine = inputSource.nextLine(); - - /* - * Handle commands we can handle. - */ - if (currentMode.canHandle(currentLine)) { - final String[] commandTokens = currentLine.split(" "); - String[] commandArgs = null; - - final int argCount = commandTokens.length; - - /* - * Parse args if they are present. - */ - if (argCount > 1) { - commandArgs = Arrays.copyOfRange(commandTokens, 1, argCount); - } - - /* - * Process command. - */ - currentMode = currentMode.process(commandTokens[0], commandArgs); - } else { - errorOutput.print("Error: Unrecognized command " + currentLine); - } - } - - normalOutput.print("Exiting now."); - } - - /** - * Set the initial command mode to use. - * - * @param initialMode - * The initial command mode to use. - */ - public void setInitialCommandMode(final CommandMode initialMode) { - if (initialMode == null) throw new NullPointerException("Initial mode must be non-zero"); - - this.initialMode = initialMode; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/Command.java b/BJC-Utils2/src/main/java/bjc/utils/cli/Command.java deleted file mode 100644 index 02bc061..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/Command.java +++ /dev/null @@ -1,39 +0,0 @@ -package bjc.utils.cli; - -/** - * Represents a command that can be invoked from a {@link CommandMode} - * - * @author ben - * - */ -public interface Command { - /** - * Create a command that serves as an alias to this one - * - * @return A command that serves as an alias to this one - */ - Command aliased(); - - /** - * Get the handler that executes this command - * - * @return The handler that executes this command - */ - CommandHandler getHandler(); - - /** - * Get the help entry for this command - * - * @return The help entry for this command - */ - CommandHelp getHelp(); - - /** - * Check if this command is an alias of another command - * - * @return Whether or not this command is an alias of another - */ - default boolean isAlias() { - return false; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/CommandHandler.java b/BJC-Utils2/src/main/java/bjc/utils/cli/CommandHandler.java deleted file mode 100644 index 2548248..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/CommandHandler.java +++ /dev/null @@ -1,24 +0,0 @@ -package bjc.utils.cli; - -import java.util.function.Function; - -/** - * A handler for a command - * - * @author ben - * - */ -@FunctionalInterface -public interface CommandHandler extends Function { - /** - * Execute this command - * - * @param args - * The arguments for this command - * @return The command mode to switch to after this command, or null to - * stop executing commands - */ - default CommandMode handle(final String[] args) { - return this.apply(args); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/CommandHelp.java b/BJC-Utils2/src/main/java/bjc/utils/cli/CommandHelp.java deleted file mode 100644 index 86567a0..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/CommandHelp.java +++ /dev/null @@ -1,31 +0,0 @@ -package bjc.utils.cli; - -/** - * Interface for the help entry for a command - * - * @author ben - * - */ -public interface CommandHelp { - /** - * Get the description of a command. - * - * @return The description of a command - */ - String getDescription(); - - /** - * Get the summary line for a command. - * - * A summary line should consist of a string of the following format - * - *
-	 * "<command-name>\t<command-summary>"
-	 * 
- * - * where anything in angle brackets should be filled in. - * - * @return The summary line line for a command - */ - String getSummary(); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/CommandMode.java b/BJC-Utils2/src/main/java/bjc/utils/cli/CommandMode.java deleted file mode 100644 index 39c72fc..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/CommandMode.java +++ /dev/null @@ -1,72 +0,0 @@ -package bjc.utils.cli; - -/** - * A mode for determining the commands that are valid to enter, and then - * handling those commands - * - * @author ben - * - */ -public interface CommandMode extends Comparable { - /** - * Check to see if this mode can handle the specified command - * - * @param command - * The command to check - * @return Whether or not this mode can handle the command. It is - * assumed not by default - */ - default boolean canHandle(final String command) { - return false; - }; - - /** - * Get the custom prompt for this mode - * - * @return the custom prompt for this mode - * - * @throws UnsupportedOperationException - * if this mode doesn't support a custom prompt - */ - default String getCustomPrompt() { - throw new UnsupportedOperationException("This mode doesn't support a custom prompt"); - } - - /** - * Get the name of this command mode - * - * @return The name of this command mode, which is the empty string by - * default - */ - public default String getName() { - return ""; - } - - /** - * Check if this mode uses a custom prompt - * - * @return Whether or not this mode uses a custom prompt - */ - default boolean isCustomPromptEnabled() { - return false; - } - - /** - * Process a command in this mode - * - * @param command - * The command to process - * @param args - * A list of arguments to the command - * @return The command mode to use for the next command. Defaults to - * returning this, and doing nothing else - */ - default CommandMode process(final String command, final String[] args) { - return this; - } - - @Override - default int compareTo(final CommandMode o) { - return getName().compareTo(o.getName()); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/DelegatingCommand.java b/BJC-Utils2/src/main/java/bjc/utils/cli/DelegatingCommand.java deleted file mode 100644 index acaa3a6..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/DelegatingCommand.java +++ /dev/null @@ -1,64 +0,0 @@ -package bjc.utils.cli; - -/** - * A class for a command that delegates to another command. - * - * @author ben - * - */ -class DelegatingCommand implements Command { - /* - * The command to delegate to. - */ - private final Command delegate; - - /** - * Create a new command that delegates to another command. - * - * @param delegate - * The command to delegate to. - */ - public DelegatingCommand(final Command delegate) { - this.delegate = delegate; - } - - @Override - public Command aliased() { - return new DelegatingCommand(delegate); - } - - @Override - public CommandHandler getHandler() { - return delegate.getHandler(); - } - - @Override - public CommandHelp getHelp() { - return delegate.getHelp(); - } - - @Override - public boolean isAlias() { - return true; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("DelegatingCommand ["); - - if (delegate != null) { - builder.append("delegate="); - builder.append(delegate); - } - - builder.append("]"); - - return builder.toString(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommand.java b/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommand.java deleted file mode 100644 index 4ae4dea..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommand.java +++ /dev/null @@ -1,84 +0,0 @@ -package bjc.utils.cli; - -/** - * Generic command implementation. - * - * @author ben - * - */ -public class GenericCommand implements Command { - /* - * The behavior for invoking the command. - */ - private final CommandHandler handler; - - /* - * The help for the command. - */ - private CommandHelp help; - - /** - * Create a new generic command. - * - * @param handler - * The handler to use for the command. - * @param description - * The description of the command. May be null, in which - * case a default is provided. - * @param help - * The detailed help message for the command. May be - * null, in which case the description is repeated for - * the detailed help. - */ - public GenericCommand(final CommandHandler handler, final String description, final String help) { - if (handler == null) throw new NullPointerException("Command handler must not be null"); - - this.handler = handler; - - if (description == null) { - this.help = new NullHelp(); - } else { - this.help = new GenericHelp(description, help); - } - } - - @Override - public Command aliased() { - return new DelegatingCommand(this); - } - - @Override - public CommandHandler getHandler() { - return handler; - } - - @Override - public CommandHelp getHelp() { - return help; - } - - @Override - public boolean isAlias() { - return false; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("GenericCommand ["); - - if (help != null) { - builder.append("help="); - builder.append(help); - } - - builder.append("]"); - - return builder.toString(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommandMode.java b/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommandMode.java deleted file mode 100644 index 8764537..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/GenericCommandMode.java +++ /dev/null @@ -1,469 +0,0 @@ -package bjc.utils.cli; - -import java.util.TreeMap; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IMap; - -/** - * A general command mode, with a customizable set of commands - * - * There is a small set of commands which is handled by default. The first is - * 'list', which lists all the commands the user can input. The second is - * 'alias', which allows the user to bind a new name to a command - * - * @author ben - * - */ -public class GenericCommandMode implements CommandMode { - /* - * Contains the commands this mode handles - */ - private final IMap commandHandlers; - private final IMap defaultHandlers; - - /* - * Contains help topics without an associated command - */ - private final IMap helpTopics; - - /* - * The action to execute upon encountering an unknown command - */ - private BiConsumer unknownCommandHandler; - - /* - * The functions to use for input/output - */ - private final Consumer errorOutput; - private final Consumer normalOutput; - - /* - * The name of this command mode, or null if it is unnamed - */ - private String modeName; - - /* - * The custom prompt to use, or null if none is specified - */ - private String customPrompt; - - /** - * Create a new generic command mode - * - * @param normalOutput - * The function to use for normal output - * @param errorOutput - * The function to use for error output - */ - public GenericCommandMode(final Consumer normalOutput, final Consumer errorOutput) { - if (normalOutput == null) - throw new NullPointerException("Normal output source must be non-null"); - else if (errorOutput == null) throw new NullPointerException("Error output source must be non-null"); - - this.normalOutput = normalOutput; - this.errorOutput = errorOutput; - - /* - * Initialize handler maps so that they sort in alphabetical - */ - /* - * order - */ - commandHandlers = new FunctionalMap<>(new TreeMap<>()); - defaultHandlers = new FunctionalMap<>(new TreeMap<>()); - helpTopics = new FunctionalMap<>(new TreeMap<>()); - - setupDefaultCommands(); - } - - /** - * Add an alias to an existing command - * - * @param commandName - * The name of the command to add an alias for - * @param aliasName - * The new alias for the command - * - * @throws IllegalArgumentException - * if the specified command doesn't have a bound - * handler, or if the alias name already has a bound - * value - */ - public void addCommandAlias(final String commandName, final String aliasName) { - if (commandName == null) - throw new NullPointerException("Command name must not be null"); - else if (aliasName == null) - throw new NullPointerException("Alias name must not be null"); - else if (!commandHandlers.containsKey(commandName) && !defaultHandlers.containsKey(commandName)) - throw new IllegalArgumentException("Cannot alias non-existant command '" + commandName + "'"); - else if (commandHandlers.containsKey(aliasName) || defaultHandlers.containsKey(aliasName)) - throw new IllegalArgumentException( - "Cannot bind alias '" + aliasName + "' to a command with a bound handler"); - else { - Command aliasedCommand; - - if (defaultHandlers.containsKey(commandName)) { - aliasedCommand = defaultHandlers.get(commandName).aliased(); - } else { - aliasedCommand = commandHandlers.get(commandName).aliased(); - } - - commandHandlers.put(aliasName, aliasedCommand); - } - } - - /** - * Add a command to this command mode - * - * @param command - * The name of the command to add - * @param handler - * The handler to use for the specified command - * - * @throws IllegalArgumentException - * if the specified command already has a handler - * registered - */ - public void addCommandHandler(final String command, final Command handler) { - if (command == null) - throw new NullPointerException("Command must not be null"); - else if (handler == null) - throw new NullPointerException("Handler must not be null"); - else if (canHandle(command)) - throw new IllegalArgumentException("Command " + command + " already has a handler registered"); - else { - commandHandlers.put(command, handler); - } - } - - /** - * Add a help topic to this command mode that isn't tied to a command - * - * @param topicName - * The name of the topic - * @param topic - * The contents of the topic - */ - public void addHelpTopic(final String topicName, final CommandHelp topic) { - helpTopics.put(topicName, topic); - } - - /* - * Default command builders - */ - - private GenericCommand buildAliasCommand() { - final String aliasShortHelp = "alias\tAlias one command to another"; - final String aliasLongHelp = "Gives a command another name it can be invoked by." - + " Invoke with two arguments: the name of the command to alias" - + "followed by the name of the alias to give that command."; - - return new GenericCommand((args) -> { - doAliasCommands(args); - - return this; - }, aliasShortHelp, aliasLongHelp); - } - - private GenericCommand buildClearCommands() { - final String clearShortHelp = "clear\tClear the screen"; - final String clearLongHelp = "Clears the screen of all the text on it," + " and prints a new prompt."; - - return new GenericCommand((args) -> { - errorOutput.accept("ERROR: This console doesn't support screen clearing"); - - return this; - }, clearShortHelp, clearLongHelp); - } - - private GenericCommand buildExitCommand() { - final String exitShortHelp = "exit\tExit the console"; - final String exitLongHelp = "First prompts the user to make sure they want to" - + " exit, then quits if they say they do"; - - return new GenericCommand((args) -> { - errorOutput.accept("ERROR: This console doesn't support auto-exiting"); - - return this; - }, exitShortHelp, exitLongHelp); - } - - private GenericCommand buildHelpCommand() { - final String helpShortHelp = "help\tConsult the help system"; - final String helpLongHelp = "Consults the internal help system." - + " Invoked in two different ways. Invoking with no arguments" - + " causes all the topics you can ask for details on to be list," - + " while invoking with the name of a topic will print the entry" + " for that topic"; - - return new GenericCommand((args) -> { - if (args == null || args.length == 0) { - /* - * Invoke general help - */ - doHelpSummary(); - } else { - /* - * Invoke help for a command - */ - doHelpCommand(args[0]); - } - - return this; - }, helpShortHelp, helpLongHelp); - } - - private GenericCommand buildListCommand() { - final String listShortHelp = "list\tList available commands"; - final String listLongHelp = "Lists all of the commands available in this mode," - + " as well as commands available in any mode"; - - return new GenericCommand((args) -> { - doListCommands(); - - return this; - }, listShortHelp, listLongHelp); - } - - @Override - public boolean canHandle(final String command) { - return commandHandlers.containsKey(command) || defaultHandlers.containsKey(command); - } - - /* - * Implement default commands - */ - - private void doAliasCommands(final String[] args) { - if (args.length != 2) { - errorOutput.accept("ERROR: Alias requires two arguments." - + " The command name, and the alias for that command"); - } else { - final String commandName = args[0]; - final String aliasName = args[1]; - - if (!canHandle(commandName)) { - errorOutput.accept("ERROR: '" + commandName + "' is not a valid command."); - } else if (canHandle(aliasName)) { - errorOutput.accept("ERROR: Cannot overwrite command '" + aliasName + "'"); - } else { - addCommandAlias(commandName, aliasName); - } - } - } - - private void doHelpCommand(final String commandName) { - if (commandHandlers.containsKey(commandName)) { - final String desc = commandHandlers.get(commandName).getHelp().getDescription(); - - normalOutput.accept("\n" + desc); - } else if (defaultHandlers.containsKey(commandName)) { - final String desc = defaultHandlers.get(commandName).getHelp().getDescription(); - - normalOutput.accept("\n" + desc); - } else if (helpTopics.containsKey(commandName)) { - normalOutput.accept("\n" + helpTopics.get(commandName).getDescription()); - } else { - errorOutput.accept( - "ERROR: I'm sorry, but there is no help available for '" + commandName + "'"); - } - } - - private void doHelpSummary() { - normalOutput.accept("Help topics for this command mode are as follows:\n"); - - if (commandHandlers.size() > 0) { - commandHandlers.forEachValue(command -> { - if (!command.isAlias()) { - normalOutput.accept("\t" + command.getHelp().getSummary() + "\n"); - } - }); - } else { - normalOutput.accept("\tNone available\n"); - } - - normalOutput.accept("\nHelp topics available in all command modes are as follows\n"); - if (defaultHandlers.size() > 0) { - defaultHandlers.forEachValue(command -> { - if (!command.isAlias()) { - normalOutput.accept("\t" + command.getHelp().getSummary() + "\n"); - } - }); - } else { - normalOutput.accept("\tNone available\n"); - } - - normalOutput.accept("\nHelp topics not associated with a command are as follows\n"); - if (helpTopics.size() > 0) { - helpTopics.forEachValue(topic -> { - normalOutput.accept("\t" + topic.getSummary() + "\n"); - }); - } else { - normalOutput.accept("\tNone available\n"); - } - } - - private void doListCommands() { - normalOutput.accept("The available commands for this mode are as follows:\n"); - - commandHandlers.keyList().forEach(commandName -> { - normalOutput.accept("\t" + commandName); - }); - - normalOutput.accept("\nThe following commands are available in all modes:\n"); - defaultHandlers.keyList().forEach(commandName -> { - normalOutput.accept("\t" + commandName); - }); - - normalOutput.accept("\n"); - } - - @Override - public String getCustomPrompt() { - if (customPrompt != null) return customPrompt; - - return CommandMode.super.getCustomPrompt(); - } - - @Override - public String getName() { - if (modeName != null) return modeName; - - return CommandMode.super.getName(); - } - - @Override - public boolean isCustomPromptEnabled() { - return customPrompt != null; - } - - @Override - public CommandMode process(final String command, final String[] args) { - normalOutput.accept("\n"); - - if (defaultHandlers.containsKey(command)) - return defaultHandlers.get(command).getHandler().handle(args); - else if (commandHandlers.containsKey(command)) - return commandHandlers.get(command).getHandler().handle(args); - else { - if (args != null) { - errorOutput.accept("ERROR: Unrecognized command " + command + String.join(" ", args)); - } else { - errorOutput.accept("ERROR: Unrecognized command " + command); - } - - if (unknownCommandHandler == null) - throw new UnsupportedOperationException("Command " + command + " is invalid."); - - unknownCommandHandler.accept(command, args); - } - - return this; - } - - /** - * Set the custom prompt for this mode - * - * @param prompt - * The custom prompt for this mode, or null to disable - * the custom prompt - */ - public void setCustomPrompt(final String prompt) { - customPrompt = prompt; - } - - /** - * Set the name of this mode - * - * @param name - * The desired name of this mode, or null to use the - * default name - */ - public void setModeName(final String name) { - modeName = name; - } - - /** - * Set the handler to use for unknown commands - * - * @param handler - * The handler to use for unknown commands, or null to - * throw on unknown commands - */ - public void setUnknownCommandHandler(final BiConsumer handler) { - if (handler == null) throw new NullPointerException("Handler must not be null"); - - unknownCommandHandler = handler; - } - - private void setupDefaultCommands() { - defaultHandlers.put("list", buildListCommand()); - defaultHandlers.put("alias", buildAliasCommand()); - defaultHandlers.put("help", buildHelpCommand()); - - addCommandAlias("help", "man"); - - /* - * Add commands handled in a upper layer. - */ - - /* - * @TODO figure out a place to put commands that apply across - */ - /* - * all - */ - /* - * modes, but only apply to a specific application - */ - defaultHandlers.put("clear", buildClearCommands()); - defaultHandlers.put("exit", buildExitCommand()); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("GenericCommandMode ["); - - if (commandHandlers != null) { - builder.append("commandHandlers="); - builder.append(commandHandlers); - } - - if (defaultHandlers != null) { - builder.append(", "); - builder.append("defaultHandlers="); - builder.append(defaultHandlers); - } - - if (helpTopics != null) { - builder.append(", "); - builder.append("helpTopics="); - builder.append(helpTopics); - } - - if (modeName != null) { - builder.append(", "); - builder.append("modeName="); - builder.append(modeName); - } - - if (customPrompt != null) { - builder.append(", "); - builder.append("customPrompt="); - builder.append(customPrompt); - } - - builder.append("]"); - - return builder.toString(); - } - -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/GenericHelp.java b/BJC-Utils2/src/main/java/bjc/utils/cli/GenericHelp.java deleted file mode 100644 index 38adf57..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/GenericHelp.java +++ /dev/null @@ -1,63 +0,0 @@ -package bjc.utils.cli; - -/** - * Generic implementation of a help topic - * - * @author ben - * - */ -public class GenericHelp implements CommandHelp { - // The strings for this help topic - private final String summary; - private final String description; - - /** - * Create a new help topic - * - * @param summary - * The summary of this help topic - * @param description - * The description of this help topic, or null if this - * help topic doesn't have a more detailed description - */ - public GenericHelp(final String summary, final String description) { - if (summary == null) throw new NullPointerException("Help summary must be non-null"); - - this.summary = summary; - this.description = description; - } - - @Override - public String getDescription() { - if (description == null) return summary; - - return description; - } - - @Override - public String getSummary() { - return summary; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - - builder.append("GenericHelp ["); - - if (summary != null) { - builder.append("summary="); - builder.append(summary); - } - - if (description != null) { - builder.append(", "); - builder.append("description="); - builder.append(description); - } - - builder.append("]"); - - return builder.toString(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/NullHelp.java b/BJC-Utils2/src/main/java/bjc/utils/cli/NullHelp.java deleted file mode 100644 index 6c49ae6..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/NullHelp.java +++ /dev/null @@ -1,20 +0,0 @@ -package bjc.utils.cli; - -/** - * Implementation of a help topic that doesn't exist - * - * @author ben - * - */ -public class NullHelp implements CommandHelp { - @Override - public String getDescription() { - return "No description provided"; - } - - @Override - public String getSummary() { - return "No summary provided"; - } - -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java b/BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java deleted file mode 100644 index ec66fe2..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java +++ /dev/null @@ -1,392 +0,0 @@ -package bjc.utils.cli.objects; - -import java.io.InputStreamReader; -import java.io.Reader; - -import java.util.HashMap; -import java.util.Map; -import java.util.Scanner; -import java.util.function.Predicate; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -import bjc.utils.ioutils.Prompter; -import bjc.utils.ioutils.blocks.*; - -import static bjc.utils.cli.objects.Command.CommandStatus; -import static bjc.utils.cli.objects.Command.CommandStatus.*; - -public class BlockReaderCLI { - private final Logger LOGGER = Logger.getLogger(BlockReaderCLI.class.getName()); - - public static class BlockReaderState { - public final Map readers; - public final Map sources; - - public BlockReaderState(Map readers, Map sources) { - this.readers = readers; - this.sources = sources; - } - } - - private BlockReaderState stat; - - /** - * Create a new CLI for configuring BlockReaders. - * - * @param srcs - * The container of initial I/O sources. - */ - public BlockReaderCLI(Map srcs) { - stat = new BlockReaderState(new HashMap<>(), srcs); - } - - public static void main(String[] args) { - /* - * Create/configure I/O sources. - */ - Map sources = new HashMap<>(); - sources.put("stdio", new InputStreamReader(System.in)); - - BlockReaderCLI reader = new BlockReaderCLI(sources); - - reader.run(new Scanner(System.in), "console", true); - } - - /** - * Run the CLI on an input source. - * - * @param input - * The place to read input from. - * @param ioSource - * The name of the place to read input from. - * @param interactive - * Whether or not the source is interactive - */ - public void run(Scanner input, String ioSource, boolean interactive) { - int lno = 0; - while(input.hasNextLine()) { - if(interactive) - System.out.printf("reader-conf(%d)>", lno); - - String ln = input.nextLine(); - - lno += 1; - - Command com = Command.fromString(ln, lno, ioSource); - if(com == null) continue; - - CommandStatus stat = handleCommand(com, interactive); - if(stat == FINISH || stat == ERROR) { - return; - } - } - - input.close(); - } - - /* - * Handle a command. - */ - public CommandStatus handleCommand(Command com, boolean interactive) { - switch(com.nameCommand) { - case "def-filtered": - return defFiltered(com); - case "def-layered": - return defLayered(com); - case "def-pushback": - return defPushback(com); - case "def-simple": - return defSimple(com); - case "def-serial": - return defSerial(com); - case "def-toggled": - return defToggled(com); - case "}": - case "end": - case "exit": - case "quit": - if(interactive) - System.out.printf("Exiting reader-conf, %d readers configured in %d commands\n", - stat.readers.size(), com.lineNo); - return FINISH; - default: - LOGGER.severe(com.error("Unknown command '%s'\n", com.nameCommand)); - return FAIL; - } - } - - private CommandStatus defFiltered(Command com) { - String remn = com.remnCommand; - - /* - * Get the block name. - */ - int idx = remn.indexOf(' '); - if(idx == -1) { - LOGGER.severe(com.error("No name argument for def-filtered.\n")); - return FAIL; - } - String blockName = remn.substring(0, idx).trim(); - remn = remn.substring(idx).trim(); - - /* - * Check there isn't a reader already bound to this name. - */ - if(stat.readers.containsKey(blockName)) { - LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName)); - } - - /* - * Get the reader name. - */ - idx = remn.indexOf(' '); - if(idx == -1) { - LOGGER.severe(com.error("No reader-name argument for def-filtered.\n")); - return FAIL; - } - String readerName = remn.substring(0, idx).trim(); - remn = remn.substring(idx).trim(); - - /* - * Check there is a reader bound to that name. - */ - if(!stat.readers.containsKey(readerName)) { - LOGGER.severe(com.error("No source named %s\n", readerName)); - return FAIL; - } - - /* - * Get the pattern. - */ - if(remn.equals("")) { - LOGGER.severe(com.error("No filter argument for def-filtered\n")); - return FAIL; - } - - String filter = remn; - - try { - Pattern pat = Pattern.compile(filter); - - Predicate pred = (block) -> { - Matcher mat = pat.matcher(block.contents); - - return mat.matches(); - }; - - BlockReader reader = new FilteredBlockReader(stat.readers.get(readerName), pred); - - stat.readers.put(blockName, reader); - } catch (PatternSyntaxException psex) { - LOGGER.severe(com.error("Invalid regular expression '%s' for filter. (%s)\n", filter, psex.getMessage())); - return FAIL; - } - - return SUCCESS; - } - - private CommandStatus defPushback(Command com) { - String[] parts = com.remnCommand.split(" "); - - if(parts.length != 2) { - LOGGER.severe(com.error("Incorrect number of arguments to def-pushback. Requires a block name and a reader name\n")); - return FAIL; - } - - String blockName = parts[0]; - if(stat.readers.containsKey(blockName)) { - LOGGER.warning(com.warn("Shadowing existing reader %s\n", blockName)); - return FAIL; - } - - String readerName = parts[1]; - if(!stat.readers.containsKey(readerName)) { - LOGGER.severe(com.error("No reader named %s\n", readerName)); - return FAIL; - } - - BlockReader reader = new PushbackBlockReader(stat.readers.get(readerName)); - stat.readers.put(blockName, reader); - - return SUCCESS; - } - - private CommandStatus defToggled(Command com) { - String[] parts = com.remnCommand.split(" "); - - if(parts.length != 3) { - LOGGER.severe(com.error("Incorrect number of arguments to def-toggled. Requires a block name and two reader names\n")); - return FAIL; - } - - /* - * Get the block name. - */ - String blockName = parts[0]; - if(stat.readers.containsKey(blockName)) { - LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName)); - } - - /* - * Make sure the component readers exist. - */ - if(!stat.readers.containsKey(parts[1])) { - LOGGER.severe(com.error("No reader named %s\n", parts[1])); - return FAIL; - } - - if(!stat.readers.containsKey(parts[2])) { - LOGGER.severe(com.error("No reader named %s\n", parts[2])); - return FAIL; - } - - BlockReader reader = new ToggledBlockReader(stat.readers.get(parts[1]), stat.readers.get(parts[2])); - stat.readers.put(blockName, reader); - - return SUCCESS; - } - - private CommandStatus defLayered(Command com) { - String[] parts = com.remnCommand.split(" "); - - if(parts.length != 3) { - LOGGER.severe(com.error("Incorrect number of arguments to def-layered. Requires a block name and two reader names\n")); - return FAIL; - } - - /* - * Get the block name. - */ - String blockName = parts[0]; - if(stat.readers.containsKey(blockName)) { - LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName)); - } - - /* - * Make sure the component readers exist. - */ - if(!stat.readers.containsKey(parts[1])) { - LOGGER.severe(com.error("No reader named %s\n", parts[1])); - return FAIL; - } - - if(!stat.readers.containsKey(parts[2])) { - LOGGER.severe(com.error("No reader named %s\n", parts[2])); - return FAIL; - } - - BlockReader reader = new LayeredBlockReader(stat.readers.get(parts[1]), stat.readers.get(parts[2])); - stat.readers.put(blockName, reader); - - return SUCCESS; - } - - private CommandStatus defSerial(Command com) { - String[] parts = com.remnCommand.split(" "); - - if(parts.length < 2) { - LOGGER.severe(com.error("Not enough arguments to def-serial. Requires at least a block name and at least one reader name\n")); - return FAIL; - } - - /* - * Get the name for this BlockReader. - */ - String blockName = parts[0]; - /* - * Check there isn't a reader already bound to this name. - */ - if(stat.readers.containsKey(blockName)) { - LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName)); - } - - /* - * Get all of the component readers. - */ - BlockReader[] readerArr = new BlockReader[parts.length - 1]; - for(int i = 1; i < parts.length; i++) { - String readerName = parts[i]; - - /* - * Check there is a reader bound to that name. - */ - if(!stat.readers.containsKey(readerName)) { - LOGGER.severe(com.error("No reader named %s\n", readerName)); - return FAIL; - } - - readerArr[i] = stat.readers.get(readerName); - } - - BlockReader reader = new SerialBlockReader(readerArr); - - stat.readers.put(blockName, reader); - - return SUCCESS; - } - - private CommandStatus defSimple(Command com) { - String remn = com.remnCommand; - - /* - * Get the block name. - */ - int idx = remn.indexOf(' '); - if(idx == -1) { - LOGGER.severe(com.error("No name argument for def-simple.\n")); - return FAIL; - } - String blockName = remn.substring(0, idx).trim(); - remn = remn.substring(idx).trim(); - - /* - * Check there isn't a reader already bound to this name. - */ - if(stat.readers.containsKey(blockName)) { - LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName)); - } - - /* - * Get the source name. - */ - idx = remn.indexOf(' '); - if(idx == -1) { - LOGGER.severe(com.error("No source-name argument for def-simple.\n")); - return FAIL; - } - String sourceName = remn.substring(0, idx).trim(); - remn = remn.substring(idx).trim(); - - /* - * Check there is a source bound to that name. - */ - if(!stat.sources.containsKey(sourceName)) { - LOGGER.severe(com.error("No source named %s\n", sourceName)); - return FAIL; - } - - /* - * Get the pattern. - */ - if(remn.equals("")) { - LOGGER.severe(com.error("No delimiter argument for def-simple\n")); - return FAIL; - } - - String delim = remn; - - try { - BlockReader reader = new SimpleBlockReader(delim, stat.sources.get(sourceName)); - - stat.readers.put(blockName, reader); - } catch (PatternSyntaxException psex) { - LOGGER.severe(com.error("Invalid regular expression '%s' for delimiter. (%s)\n", delim, psex.getMessage())); - return FAIL; - } - - return SUCCESS; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java b/BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java deleted file mode 100644 index e605a2b..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java +++ /dev/null @@ -1,87 +0,0 @@ -package bjc.utils.cli.objects; - -public class Command { - /** - * Command status values. - */ - public static enum CommandStatus { - /** - * The command succeded. - */ - SUCCESS, - /** - * The command failed non-fatally. - */ - FAIL, - /** - * The command failed fatally. - */ - ERROR, - /** - * The command was the last one. - */ - FINISH, - } - - public final int lineNo; - - public final String fullCommand; - public final String remnCommand; - public final String nameCommand; - - public final String ioSource; - - /** - * Create a new command. - * - * @param ln - * The line to get the command from. - * @param lno - * The number of the line the command came from. - * @param ioSrc - * The name of where the I/O came from. - */ - public Command(String ln, int lno, String ioSrc) { - int idx = ln.indexOf(' '); - - if(idx == -1) idx = ln.length(); - - fullCommand = ln; - nameCommand = ln.substring(0, idx).trim(); - remnCommand = ln.substring(idx).trim(); - - lineNo = lno; - - ioSource = ioSrc; - } - - public static Command fromString(String ln, int lno, String ioSource) { - /* - * Ignore blank lines and comments. - */ - if(ln.equals("")) return null; - if(ln.startsWith("#")) return null; - - /* - * Trim off comments part-way through the line. - */ - int idxHash = ln.indexOf('#'); - if(idxHash != -1) { - ln = ln.substring(0, idxHash).trim(); - } - - return new Command(ln, lno, ioSource); - } - - public String warn(String warning, Object... parms) { - String msg = String.format(warning, parms); - - return String.format("WARNING (%s:%d): %s", ioSource, lineNo, msg); - } - - public String error(String err, Object... parms) { - String msg = String.format(err, parms); - - return String.format("ERROR (%s:%d): %s", ioSource, lineNo, msg); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/DefineCLI.java b/BJC-Utils2/src/main/java/bjc/utils/cli/objects/DefineCLI.java deleted file mode 100644 index bb2733f..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/DefineCLI.java +++ /dev/null @@ -1,133 +0,0 @@ -package bjc.utils.cli.objects; - -import java.util.HashMap; -import java.util.Map; -import java.util.Scanner; -import java.util.function.UnaryOperator; -import java.util.logging.Logger; -import java.util.regex.Pattern; - -import static bjc.utils.cli.objects.Command.CommandStatus; -import static bjc.utils.cli.objects.Command.CommandStatus.*; - -public class DefineCLI { - private final Logger LOGGER = Logger.getLogger(DefineCLI.class.getName()); - - public static class DefineState { - public final Map> defines; - - public final Map strings; - public final Map formats; - - public final Map patterns; - - public DefineState() { - this(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>()); - } - - public DefineState(Map> defines, - Map strings, Map formats, - Map patterns) { - this.defines = defines; - - this.strings = strings; - this.formats = formats; - - this.patterns = patterns; - } - } - - private DefineState stat; - - public DefineCLI() { - stat = new DefineState(); - } - - public static void main(String[] args) { - DefineCLI defin = new DefineCLI(); - } - - /** - * Run the CLI on an input source. - * - * @param input - * The place to read input from. - * @param ioSource - * The name of the place to read input from. - * @param interactive - * Whether or not the source is interactive - */ - public void run(Scanner input, String ioSource, boolean interactive) { - int lno = 0; - while(input.hasNextLine()) { - if(interactive) - System.out.printf("define-conf(%d)>", lno); - - String ln = input.nextLine(); - - lno += 1; - - Command com = Command.fromString(ln, lno, ioSource); - if(com == null) continue; - - handleCommand(com, interactive); - } - - input.close(); - } - - public void handleCommand(Command com, boolean interactive) { - switch(com.nameCommand) { - case "def-string": - default: - LOGGER.severe(com.error("Unknown command %s\n", com.nameCommand)); - break; - } - } - - private CommandStatus defString(Command com) { - String remn = com.remnCommand; - - int idx = remn.indexOf(' '); - if(idx == -1) { - LOGGER.warning(com.warn("Binding empty string to name '%s'\n", remn)); - idx = remn.length(); - } - String name = remn.substring(0, idx); - String strang = remn.substring(idx); - - if(stat.strings.containsKey(name)) { - LOGGER.warning(com.warn("Shadowing string '%s'\n", name)); - } - - stat.strings.put(name, strang); - - return SUCCESS; - } - - private CommandStatus defFormat(Command com) { - String remn = com.remnCommand; - - int idx = remn.indexOf(' '); - if(idx == -1) { - LOGGER.warning(com.warn("Binding empty format to name '%s'\n", remn)); - idx = remn.length(); - } - String name = remn.substring(0, idx); - String fmt = remn.substring(idx); - - if(stat.formats.containsKey(name)) { - LOGGER.warning(com.warn("Shadowing format '%s'\n", name)); - } - - stat.formats.put(name, fmt); - - return SUCCESS; - } - - private CommandStatus bindFormat(Command com) { - String[] parts = com.remnCommand.split(" "); - - return SUCCESS; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescription.java b/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescription.java deleted file mode 100644 index 28f81d1..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescription.java +++ /dev/null @@ -1,135 +0,0 @@ -package bjc.utils.components; - -/** - * Generic implementation of a description for a component - * - * @author ben - * - */ -public class ComponentDescription implements IDescribedComponent { - private static void sanityCheckArgs(final String name, final String author, final String description, - final int version) { - if (name == null) - throw new NullPointerException("Component name can't be null"); - else if (version <= 0) throw new IllegalArgumentException("Component version must be greater than 0"); - } - - /** - * The author of the component - */ - private final String author; - /** - * The description of the component - */ - private final String description; - /** - * The name of the component - */ - private final String name; - - /** - * The version of the component - */ - private final int version; - - /** - * Create a new component description - * - * @param name - * The name of the component - * @param author - * The author of the component - * @param description - * The description of the component - * @param version - * The version of the component - * @throws IllegalArgumentException - * thrown if version is less than 1 - */ - public ComponentDescription(final String name, final String author, final String description, - final int version) { - sanityCheckArgs(name, author, description, version); - - this.name = name; - this.author = author; - this.description = description; - this.version = version; - } - - @Override - public String getAuthor() { - if (author == null) return IDescribedComponent.super.getAuthor(); - - return author; - } - - @Override - public String getDescription() { - if (description == null) return IDescribedComponent.super.getDescription(); - - return description; - } - - @Override - public String getName() { - return name; - } - - @Override - public int getVersion() { - return version; - } - - @Override - public String toString() { - return name + " component v" + version + ", written by " + author; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + (author == null ? 0 : author.hashCode()); - result = prime * result + (description == null ? 0 : description.hashCode()); - result = prime * result + (name == null ? 0 : name.hashCode()); - result = prime * result + version; - - return result; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - - final ComponentDescription other = (ComponentDescription) obj; - - if (author == null) { - if (other.author != null) return false; - } else if (!author.equals(other.author)) return false; - - if (description == null) { - if (other.description != null) return false; - } else if (!description.equals(other.description)) return false; - - if (name == null) { - if (other.name != null) return false; - } else if (!name.equals(other.name)) return false; - - if (version != other.version) return false; - - return true; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java b/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java deleted file mode 100644 index f7ddaff..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java +++ /dev/null @@ -1,65 +0,0 @@ -package bjc.utils.components; - -import static bjc.utils.ioutils.RuleBasedReaderPragmas.buildInteger; -import static bjc.utils.ioutils.RuleBasedReaderPragmas.buildStringCollapser; - -import java.io.InputStream; - -import bjc.utils.ioutils.RuleBasedConfigReader; - -/** - * Read a component description from a file - * - * @author ben - * - */ -public class ComponentDescriptionFileParser { - // The reader used to read in component descriptions - private static RuleBasedConfigReader reader; - - // Initialize the reader and its pragmas - static { - // This reader works entirely off of pragmas, so no need to - // handle - // rules - reader = new RuleBasedConfigReader<>((tokenizer, statePair) -> { - // Don't need to do anything on rule start - }, (tokenizer, state) -> { - // Don't need to do anything on rule continuation - }, (state) -> { - // Don't need to do anything on rule end - }); - - setupReaderPragmas(); - } - - /** - * Parse a component description from a stream - * - * @param inputSource - * The stream to parse from - * @return The description parsed from the stream - */ - public static ComponentDescription fromStream(final InputStream inputSource) { - if (inputSource == null) throw new NullPointerException("Input source must not be null"); - - final ComponentDescriptionState readState = reader.fromStream(inputSource, - new ComponentDescriptionState()); - - return readState.toDescription(); - } - - /* - * Create all the pragmas the reader needs to function - */ - private static void setupReaderPragmas() { - reader.addPragma("name", buildStringCollapser("name", (name, state) -> state.setName(name))); - - reader.addPragma("author", buildStringCollapser("author", (author, state) -> state.setAuthor(author))); - - reader.addPragma("description", buildStringCollapser("description", - (description, state) -> state.setDescription(description))); - - reader.addPragma("version", buildInteger("version", (version, state) -> state.setVersion(version))); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionState.java b/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionState.java deleted file mode 100644 index 8d66f85..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionState.java +++ /dev/null @@ -1,144 +0,0 @@ -package bjc.utils.components; - -/** - * Internal state of component description parser - * - * @author ben - * - */ -public class ComponentDescriptionState { - // Tentative name of this component - private String name; - - // Tentative description of this componet - private String description; - - // Tentative author of this component - private String author; - - // Tentative version of this component - private int version; - - /** - * Set the author of this component - * - * @param author - * The author of this component - */ - public void setAuthor(final String author) { - this.author = author; - } - - /** - * Set the description of this component - * - * @param description - * The description of this component - */ - public void setDescription(final String description) { - this.description = description; - } - - /** - * Set the name of this component - * - * @param name - * The name of this component - */ - public void setName(final String name) { - this.name = name; - } - - /** - * Set the version of this component - * - * @param version - * The version of this component - */ - public void setVersion(final int version) { - this.version = version; - } - - /** - * Convert this state into the description it represents - * - * @return The description represented by this state - */ - public ComponentDescription toDescription() { - return new ComponentDescription(name, author, description, version); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + (author == null ? 0 : author.hashCode()); - result = prime * result + (description == null ? 0 : description.hashCode()); - result = prime * result + (name == null ? 0 : name.hashCode()); - result = prime * result + version; - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - - final ComponentDescriptionState other = (ComponentDescriptionState) obj; - - if (author == null) { - if (other.author != null) return false; - } else if (!author.equals(other.author)) return false; - - if (description == null) { - if (other.description != null) return false; - } else if (!description.equals(other.description)) return false; - - if (name == null) { - if (other.name != null) return false; - } else if (!name.equals(other.name)) return false; - - if (version != other.version) return false; - - return true; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("ComponentDescriptionState ["); - - if (name != null) { - builder.append("name="); - builder.append(name); - builder.append(", "); - } - - if (description != null) { - builder.append("description="); - builder.append(description); - builder.append(", "); - } - - if (author != null) { - builder.append("author="); - builder.append(author); - builder.append(", "); - } - - builder.append("version="); - builder.append(version); - builder.append("]"); - - return builder.toString(); - } - -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/components/FileComponentRepository.java b/BJC-Utils2/src/main/java/bjc/utils/components/FileComponentRepository.java deleted file mode 100644 index efde5c7..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/components/FileComponentRepository.java +++ /dev/null @@ -1,181 +0,0 @@ -package bjc.utils.components; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.function.BiPredicate; -import java.util.function.Function; -import java.util.logging.Level; -import java.util.logging.Logger; - -import bjc.utils.data.IHolder; -import bjc.utils.data.Identity; -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.IMap; -import bjc.utils.funcutils.FileUtils; - -/** - * A component repository that loads its components from files in a directory - * - * @author ben - * - * @param - * The type of component being read in - */ -public class FileComponentRepository - implements IComponentRepository { - // The logger to use for storing data about this class - private static final Logger CLASS_LOGGER = Logger.getLogger("FileComponentRepository"); - - // The internal storage of components - private IMap components; - - // The path that all the components came from - private Path sourceDirectory; - - /** - * Create a new component repository sourcing components from files in a - * directory - * - * An exception thrown during the loading of a component will only cause - * the loading of that component to fail, but a warning will be logged. - * - * @param directory - * The directory to read component files from - * @param componentReader - * The function to use to convert files to components - */ - public FileComponentRepository(final File directory, - final Function componentReader) { - // Make sure we have valid arguments - if (directory == null) - throw new NullPointerException("Directory must not be null"); - else if (!directory.isDirectory()) - throw new IllegalArgumentException("File " + directory + " is not a directory.\n" - + "Components can only be read from a directory"); - else if (componentReader == null) throw new NullPointerException("Component reader must not be null"); - - // Initialize our fields - components = new FunctionalMap<>(); - sourceDirectory = directory.toPath().toAbsolutePath(); - - // Marker for making sure we don't skip the parent - final IHolder isFirstDir = new Identity<>(true); - - // Predicate to use to traverse all the files in a directory, - // but - // not recurse into sub-directories - final BiPredicate firstLevelTraverser = (pth, attr) -> { - if (attr.isDirectory() && !isFirstDir.getValue()) /* - * Skip - * directories, - * they - * probably - * have - * component - * support - * files. - */ - return false; - - /* - * Don't skip the first directory, that's the parent - * directory - */ - isFirstDir.replace(false); - - return true; - }; - - // Try reading components - try { - FileUtils.traverseDirectory(sourceDirectory, firstLevelTraverser, (pth, attr) -> { - loadComponent(componentReader, pth); - - // Keep loading components, even if this one - // failed - return true; - }); - } catch (final IOException ioex) { - CLASS_LOGGER.log(Level.WARNING, ioex, () -> "Error found reading component from file."); - } - } - - @Override - public IMap getAll() { - return components; - } - - @Override - public ComponentType getByName(final String name) { - return components.get(name); - } - - @Override - public IList getList() { - return components.valueList(); - } - - @Override - public String getSource() { - return "Components read from directory " + sourceDirectory + "."; - } - - /* - * Load a component from a file - */ - private void loadComponent(final Function componentReader, final Path pth) { - try { - // Try to load the component - final ComponentType component = componentReader.apply(pth.toFile()); - - if (component == null) - throw new NullPointerException("Component reader read null component"); - else if (!components.containsKey(component.getName())) { - // We only care about the latest version of a - // component - final ComponentType oldComponent = components.put(component.getName(), component); - - if (oldComponent.getVersion() > component.getVersion()) { - components.put(oldComponent.getName(), oldComponent); - } - } else { - CLASS_LOGGER.warning("Found a duplicate component.\n" - + "Multiple versions of the same component are not currently supported.\n" - + "Only the latest version of the component" + component - + " will be registered ."); - } - } catch (final Exception ex) { - CLASS_LOGGER.log(Level.WARNING, ex, () -> "Error found reading component from file " - + pth.toString() + ". This component will not be loaded"); - } - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("FileComponentRepository ["); - - if (components != null) { - builder.append("components="); - builder.append(components); - builder.append(", "); - } - - if (sourceDirectory != null) { - builder.append("sourceDirectory="); - builder.append(sourceDirectory); - } - - builder.append("]"); - - return builder.toString(); - } -} \ 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 deleted file mode 100644 index 6ee51f3..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/components/IComponentRepository.java +++ /dev/null @@ -1,49 +0,0 @@ -package bjc.utils.components; - -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.IMap; - -/** - * A collection of implementations of a particular type of - * {@link IDescribedComponent} - * - * @author ben - * - * @param - * The type of components contained in this repository - */ -public interface IComponentRepository { - /** - * Get all of the components this repository knows about - * - * @return A map from component name to component, containing all of the - * components in the repositories - */ - public IMap getAll(); - - /** - * Get a component with a specific name - * - * @param name - * The name of the component to retrieve - * @return The named component, or null if no component with that name - * exists - */ - public ComponentType getByName(String name); - - /** - * Get a list of all the registered components - * - * @return A list of all the registered components - */ - public default IList getList() { - return getAll().valueList(); - } - - /** - * Get the source from which these components came - * - * @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 deleted file mode 100644 index 952b375..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/components/IDescribedComponent.java +++ /dev/null @@ -1,64 +0,0 @@ -package bjc.utils.components; - -/** - * Represents a optional component that has status information associated with - * it - * - * @author ben - * - */ -public interface IDescribedComponent extends Comparable { - /** - * Get the author of this component - * - * Providing this is optional, with "Anonymous" as the default author - * - * @return The author of the component - */ - default String getAuthor() { - return "Anonymous"; - } - - /** - * Get the description of this component - * - * Providing this is optional, with the default being a note that no - * description was provided - * - * @return The description of the component - */ - default String getDescription() { - return "No description provided."; - } - - /** - * Get the name of this component. - * - * This is the only thing required of all components - * - * @return The name of the component - */ - String getName(); - - /** - * Get the version of this component - * - * Providing this is optional, with "1" as the default version - * - * @return The version of this component - */ - default int getVersion() { - return 1; - } - - @Override - default int compareTo(final IDescribedComponent o) { - int res = getName().compareTo(o.getName()); - - if (res == 0) { - res = getVersion() - o.getVersion(); - } - - return res; - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/BooleanToggle.java b/BJC-Utils2/src/main/java/bjc/utils/data/BooleanToggle.java deleted file mode 100644 index 12e3b2e..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/BooleanToggle.java +++ /dev/null @@ -1,76 +0,0 @@ -package bjc.utils.data; - -/** - * A simple {@link ValueToggle} that swaps between true and false. - * - * @author EVE - * - */ -public class BooleanToggle implements Toggle { - private boolean val; - - /** - * Create a new, initially false, flip-flop. - */ - public BooleanToggle() { - this(false); - } - - /** - * Create a flip-flop with the specified initial value. - * - * @param initial - * The initial value of the flip-flop. - */ - public BooleanToggle(final boolean initial) { - val = initial; - } - - @Override - public Boolean get() { - final boolean res = val; - - val = !res; - - return res; - } - - @Override - public Boolean peek() { - return val; - } - - @Override - public void set(final boolean vl) { - val = vl; - } - - @Override - public int hashCode() { - final int prime = 31; - - int result = 1; - - result = prime * result + (val ? 1231 : 1237); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof BooleanToggle)) return false; - - final BooleanToggle other = (BooleanToggle) obj; - - if (val != other.val) return false; - - return true; - } - - @Override - public String toString() { - return String.format("BooleanToggle [val=%s]", val); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/CircularIterator.java b/BJC-Utils2/src/main/java/bjc/utils/data/CircularIterator.java deleted file mode 100644 index a708eba..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/CircularIterator.java +++ /dev/null @@ -1,81 +0,0 @@ -package bjc.utils.data; - -import java.util.Iterator; - -/** - * An iterator that repeats elements from a provided iterable. - * - * @author EVE - * - * @param - * The type of the iterable. - */ -public class CircularIterator implements Iterator { - /* - * The iterable, and our current iterator into it. - */ - private Iterable source; - private Iterator curr; - - /* - * Our current element. - */ - private E curElm; - - /* - * Should we actually get new iterators, or just repeat the last - * element? - */ - private boolean doCircle; - - /** - * Create a new circular iterator. - * - * @param src - * The iterable to iterate from. - * - * @param circ - * Should we actually do circular iteration, or just - * repeat the terminal element? - */ - public CircularIterator(final Iterable src, final boolean circ) { - source = src; - curr = source.iterator(); - - doCircle = circ; - } - - /** - * Create a new circular iterator that does actual circular iteration. - * - * @param src - * The iterable to iterate from. - */ - public CircularIterator(final Iterable src) { - this(src, true); - } - - @Override - public boolean hasNext() { - // We always have something - return true; - } - - @Override - public E next() { - if (!curr.hasNext()) { - if (doCircle) { - curr = source.iterator(); - } else return curElm; - } - - curElm = curr.next(); - - return curElm; - } - - @Override - public void remove() { - curr.remove(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/Either.java b/BJC-Utils2/src/main/java/bjc/utils/data/Either.java deleted file mode 100644 index 36b3324..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/Either.java +++ /dev/null @@ -1,173 +0,0 @@ -package bjc.utils.data; - -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * Represents a pair where only one side has a value - * - * @author ben - * @param - * The type that could be on the left - * @param - * The type that could be on the right - * - */ -public class Either implements IPair { - /** - * Create a new either with the left value occupied - * - * @param - * The type of the left value - * @param - * The type of the empty right value - * @param left - * The value to put on the left - * @return An either with the left side occupied - */ - public static Either left(final LeftType left) { - return new Either<>(left, null); - } - - /** - * Create a new either with the right value occupied - * - * @param - * The type of the empty left value - * @param - * The type of the right value - * @param right - * The value to put on the right - * @return An either with the right side occupied - */ - public static Either right(final RightType right) { - return new Either<>(null, right); - } - - private LeftType leftVal; - - private RightType rightVal; - - private boolean isLeft; - - private Either(final LeftType left, final RightType right) { - if (left == null) { - rightVal = right; - } else { - leftVal = left; - - isLeft = true; - } - } - - @Override - public IPair bind( - final BiFunction> binder) { - if (binder == null) throw new NullPointerException("Binder must not be null"); - - return binder.apply(leftVal, rightVal); - } - - @Override - public IPair bindLeft( - final Function> leftBinder) { - if (leftBinder == null) throw new NullPointerException("Left binder must not be null"); - - if (isLeft) return leftBinder.apply(leftVal); - - return new Either<>(null, rightVal); - } - - @Override - public IPair bindRight( - final Function> rightBinder) { - if (rightBinder == null) throw new NullPointerException("Right binder must not be null"); - - if (isLeft) return new Either<>(leftVal, null); - - return rightBinder.apply(rightVal); - } - - @Override - public IPair combine( - final IPair otherPair, - final BiFunction leftCombiner, - final BiFunction rightCombiner) { - if (otherPair == null) - throw new NullPointerException("Other pair must not be null"); - else if (leftCombiner == null) - throw new NullPointerException("Left combiner must not be null"); - else if (rightCombiner == null) throw new NullPointerException("Right combiner must not be null"); - - if (isLeft) return otherPair.bind((otherLeft, otherRight) -> { - return new Either<>(leftCombiner.apply(leftVal, otherLeft), null); - }); - - return otherPair.bind((otherLeft, otherRight) -> { - return new Either<>(null, rightCombiner.apply(rightVal, otherRight)); - }); - } - - @Override - public IPair mapLeft(final Function mapper) { - if (mapper == null) throw new NullPointerException("Mapper must not be null"); - - if (isLeft) return new Either<>(mapper.apply(leftVal), null); - - return new Either<>(null, rightVal); - } - - @Override - public IPair mapRight(final Function mapper) { - if (mapper == null) throw new NullPointerException("Mapper must not be null"); - - if (isLeft) return new Either<>(leftVal, null); - - return new Either<>(null, mapper.apply(rightVal)); - } - - @Override - public MergedType merge(final BiFunction merger) { - if (merger == null) throw new NullPointerException("Merger must not be null"); - - return merger.apply(leftVal, rightVal); - } - - @Override - public int hashCode() { - final int prime = 31; - - int result = 1; - result = prime * result + (isLeft ? 1231 : 1237); - result = prime * result + (leftVal == null ? 0 : leftVal.hashCode()); - result = prime * result + (rightVal == null ? 0 : rightVal.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof Either)) return false; - - final Either other = (Either) obj; - - if (isLeft != other.isLeft) return false; - - if (leftVal == null) { - if (other.leftVal != null) return false; - } else if (!leftVal.equals(other.leftVal)) return false; - - if (rightVal == null) { - if (other.rightVal != null) return false; - } else if (!rightVal.equals(other.rightVal)) return false; - - return true; - } - - @Override - public String toString() { - return String.format("Either [leftVal='%s', rightVal='%s', isLeft=%s]", leftVal, rightVal, isLeft); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/GeneratingIterator.java b/BJC-Utils2/src/main/java/bjc/utils/data/GeneratingIterator.java deleted file mode 100644 index 9abca7c..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/GeneratingIterator.java +++ /dev/null @@ -1,53 +0,0 @@ -package bjc.utils.data; - -import java.util.Iterator; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; - -/** - * An iterator that generates a series of elements from a single element. - * - * @author bjculkin - * - * @param - * The type of element generated. - */ -public class GeneratingIterator implements Iterator { - private E state; - - private UnaryOperator transtion; - - private Predicate stpper; - - /** - * Create a new generative iterator. - * - * @param initial - * The initial state of the generator. - * - * @param transition - * The function to apply to the state. - * - * @param stopper - * The predicate applied to the current state to - * determine when to stop. - */ - public GeneratingIterator(E initial, UnaryOperator transition, Predicate stopper) { - state = initial; - transtion = transition; - stpper = stopper; - } - - @Override - public boolean hasNext() { - return stpper.test(state); - } - - @Override - public E next() { - state = transtion.apply(state); - - return state; - } - -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/IHolder.java b/BJC-Utils2/src/main/java/bjc/utils/data/IHolder.java deleted file mode 100644 index ca0b2ba..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/IHolder.java +++ /dev/null @@ -1,153 +0,0 @@ -package bjc.utils.data; - -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.UnaryOperator; - -import bjc.utils.data.internals.BoundListHolder; -import bjc.utils.data.internals.WrappedLazy; -import bjc.utils.data.internals.WrappedOption; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.theory.Functor; - -/** - * A holder of a single value. - * - * @author ben - * - * @param - * The type of value held - */ -public interface IHolder extends Functor { - /** - * Bind a function across the value in this container - * - * @param - * The type of value in this container - * @param binder - * The function to bind to the value - * @return A holder from binding the value - */ - public IHolder bind(Function> binder); - - /** - * Apply an action to the value - * - * @param action - * The action to apply to the value - */ - public default void doWith(final Consumer action) { - transform(value -> { - action.accept(value); - - return value; - }); - } - - @Override - default Function, Functor> fmap( - final Function func) { - return argumentFunctor -> { - if (!(argumentFunctor instanceof IHolder)) { - final String msg = "This functor only supports mapping over instances of IHolder"; - - throw new IllegalArgumentException(msg); - } - - final IHolder holder = (IHolder) argumentFunctor; - - return holder.map(func); - }; - } - - @Override - public default ContainedType getValue() { - return unwrap(value -> value); - } - - /** - * Lifts a function to bind over this holder - * - * @param - * The type of the functions return - * @param func - * The function to lift over the holder - * @return The function lifted over the holder - */ - public Function> lift(Function func); - - /** - * Make this holder lazy - * - * @return A lazy version of this holder - */ - public default IHolder makeLazy() { - return new WrappedLazy<>(this); - } - - /** - * Make this holder a list - * - * @return A list version of this holder - */ - public default IHolder makeList() { - return new BoundListHolder<>(new FunctionalList<>(this)); - } - - /** - * Make this holder optional - * - * @return An optional version of this holder - */ - public default IHolder makeOptional() { - return new WrappedOption<>(this); - } - - /** - * Create a new holder with a mapped version of the value in this - * holder. - * - * Does not change the internal state of this holder - * - * @param - * The type of the mapped value - * @param mapper - * The function to do mapping with - * @return A holder with the mapped value - */ - public IHolder map(Function mapper); - - /** - * Replace the held value with a new one - * - * @param newValue - * The value to hold instead - * @return The holder itself - */ - public default IHolder replace(final ContainedType newValue) { - return transform(oldValue -> { - return newValue; - }); - } - - /** - * Transform the value held in this holder - * - * @param transformer - * The function to transform the value with - * @return The holder itself, for easy chaining - */ - public IHolder transform(UnaryOperator transformer); - - /** - * Unwrap the value contained in this holder so that it is no longer - * held - * - * @param - * The type of the unwrapped value - * @param unwrapper - * The function to use to unwrap the value - * @return The unwrapped held value - */ - public UnwrappedType unwrap(Function unwrapper); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/IPair.java b/BJC-Utils2/src/main/java/bjc/utils/data/IPair.java deleted file mode 100644 index db8a1cb..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/IPair.java +++ /dev/null @@ -1,200 +0,0 @@ -package bjc.utils.data; - -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Function; - -import bjc.utils.funcdata.theory.Bifunctor; - -/** - * Represents a pair of values - * - * @author ben - * @param - * The type of the left side of the pair - * @param - * The type of the right side of the pair - * - */ -public interface IPair extends Bifunctor { - /** - * Bind a function across the values in this pair - * - * @param - * The type of the bound left - * @param - * The type of the bound right - * @param binder - * The function to bind with - * @return The bound pair - */ - public IPair bind( - BiFunction> binder); - - /** - * Bind a function to the left value in this pair - * - * @param - * The type of the bound value - * @param leftBinder - * The function to use to bind - * @return A pair with the left type bound - */ - public IPair bindLeft( - Function> leftBinder); - - /** - * Bind a function to the right value in this pair - * - * @param - * The type of the bound value - * @param rightBinder - * The function to use to bind - * @return A pair with the right type bound - */ - public IPair bindRight( - Function> rightBinder); - - /** - * Pairwise combine two pairs together - * - * @param - * The left type of the other pair - * @param - * The right type of the other pair - * @param otherPair - * The pair to combine with - * @return The pairs, pairwise combined together - */ - public default IPair, IPair> combine( - final IPair otherPair) { - return combine(otherPair, Pair::new, Pair::new); - } - - /** - * Combine the contents of two pairs together - * - * @param - * The type of the left value of the other pair - * @param - * The type of the right value of the other pair - * @param - * The type of the left value of the combined pair - * @param - * The type of the right value of the combined pair - * @param otherPair - * The other pair to combine with - * @param leftCombiner - * @param rightCombiner - * @return A pair with its values combined - */ - public IPair combine( - IPair otherPair, - BiFunction leftCombiner, - BiFunction rightCombiner); - - /** - * Immediately perfom the specified action with the contents of this - * pair - * - * @param consumer - * The action to perform on the pair - */ - public default void doWith(final BiConsumer consumer) { - merge((leftValue, rightValue) -> { - consumer.accept(leftValue, rightValue); - - return null; - }); - } - - @Override - default LeftBifunctorMap fmapLeft( - final Function func) { - return argumentPair -> { - if (!(argumentPair instanceof IPair)) { - final String msg = "This function can only be applied to instances of IPair"; - - throw new IllegalArgumentException(msg); - } - - final IPair argPair = (IPair) argumentPair; - - return argPair.mapLeft(func); - }; - } - - @Override - default RightBifunctorMap - - fmapRight(final Function func) { - return argumentPair -> { - if (!(argumentPair instanceof IPair)) { - final String msg = "This function can only be applied to instances of IPair"; - - throw new IllegalArgumentException(msg); - } - - final IPair argPair = (IPair) argumentPair; - - return argPair.mapRight(func); - }; - } - - /** - * Get the value on the left side of the pair - * - * @return The value on the left side of the pair - */ - @Override - public default LeftType getLeft() { - return merge((leftValue, rightValue) -> leftValue); - } - - /** - * Get the value on the right side of the pair - * - * @return The value on the right side of the pair - */ - @Override - public default RightType getRight() { - return merge((leftValue, rightValue) -> rightValue); - } - - /** - * Transform the value on the left side of the pair. Doesn't modify the - * pair - * - * @param - * The new type of the left part of the pair - * @param mapper - * The function to use to transform the left part of the - * pair - * @return The pair, with its left part transformed - */ - public IPair mapLeft(Function mapper); - - /** - * Transform the value on the right side of the pair. Doesn't modify the - * pair - * - * @param - * The new type of the right part of the pair - * @param mapper - * The function to use to transform the right part of the - * pair - * @return The pair, with its right part transformed - */ - public IPair mapRight(Function mapper); - - /** - * Merge the two values in this pair into a single value - * - * @param - * The type of the single value - * @param merger - * The function to use for merging - * @return The pair, merged into a single value - */ - public MergedType merge(BiFunction merger); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/ITree.java b/BJC-Utils2/src/main/java/bjc/utils/data/ITree.java deleted file mode 100644 index ff374e8..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/ITree.java +++ /dev/null @@ -1,234 +0,0 @@ -package bjc.utils.data; - -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; - -import bjc.utils.funcdata.bst.TreeLinearizationMethod; -import bjc.utils.functypes.ListFlattener; - -/** - * A node in a homogeneous tree with a unlimited amount of children. - * - * @author ben - * - * @param - * The type of data contained in the tree nodes. - * - */ -public interface ITree { - /** - * Append a child to this node. - * - * @param child - * The child to append to this node. - */ - void addChild(ITree child); - - /** - * Prepend a child to this node. - * - * @param child - * The child to prepend to this node. - */ - void prependChild(ITree child); - - /** - * Collapse a tree into a single version. - * - * @param - * The intermediate type being folded. - * - * @param - * The type that is the end result. - * - * @param leafTransform - * The function to use to convert leaf values. - * - * @param nodeCollapser - * The function to use to convert internal nodes and - * their children. - * - * @param resultTransformer - * The function to use to convert a state to the returned - * version. - * - * @return The final transformed state. - */ - ReturnedType collapse(Function leafTransform, - Function> nodeCollapser, - Function resultTransformer); - - /** - * Execute a given action for each of this tree's children. - * - * @param action - * The action to execute for each child. - */ - void doForChildren(Consumer> action); - - /** - * Expand the nodes of a tree into trees, and then merge the contents of - * those trees into a single tree. - * - * @param mapper - * The function to use to map values into trees. - * - * @return A tree, with some nodes expanded into trees. - */ - default ITree flatMapTree(final Function> mapper) { - return topDownTransform(dat -> TopDownTransformResult.PUSHDOWN, node -> { - if (node.getChildrenCount() > 0) { - final ITree parent = node.transformHead(mapper); - - node.doForChildren(parent::addChild); - - return parent; - } - - return node.transformHead(mapper); - }); - } - - /** - * Get the specified child of this tree. - * - * @param childNo - * The number of the child to get. - * - * @return The specified child of this tree. - */ - default ITree getChild(final int childNo) { - return transformChild(childNo, child -> child); - } - - /** - * Get a count of the number of direct children this node has. - * - * @return The number of direct children this node has. - */ - int getChildrenCount(); - - /** - * Get the data stored in this node. - * - * @return The data stored in this node. - */ - default ContainedType getHead() { - return transformHead(head -> head); - } - - /** - * Rebuild the tree with the same structure, but different nodes. - * - * @param - * The type of the new tree. - * - * @param leafTransformer - * The function to use to transform leaf tokens. - * - * @param operatorTransformer - * The function to use to transform internal tokens. - * - * @return The tree, with the nodes changed. - */ - ITree rebuildTree(Function leafTransformer, - Function operatorTransformer); - - /** - * Transform some of the nodes in this tree. - * - * @param nodePicker - * The predicate to use to pick nodes to transform. - * - * @param transformer - * The function to use to transform picked nodes. - */ - void selectiveTransform(Predicate nodePicker, UnaryOperator transformer); - - /** - * Do a top-down transform of the tree. - * - * @param transformPicker - * The function to use to pick how to progress. - * - * @param transformer - * The function used to transform picked subtrees. - * - * @return The tree with the transform applied to picked subtrees. - */ - ITree topDownTransform(Function transformPicker, - UnaryOperator> transformer); - - /** - * Transform one of this nodes children. - * - * @param - * The type of the transformed value. - * - * @param childNo - * The number of the child to transform. - * - * @param transformer - * The function to use to transform the value. - * - * @return The transformed value. - * - * @throws IllegalArgumentException - * if the childNo is out of bounds (0 <= childNo <= - * childCount()). - */ - TransformedType transformChild(int childNo, - Function, TransformedType> transformer); - - /** - * Transform the value that is the head of this node. - * - * @param - * The type of the transformed value. - * - * @param transformer - * The function to use to transform the value. - * - * @return The transformed value. - */ - TransformedType transformHead(Function transformer); - - /** - * Transform the tree into a tree with a different type of token. - * - * @param - * The type of the new tree. - * - * @param transformer - * The function to use to transform tokens. - * - * @return A tree with the token types transformed. - */ - default ITree transformTree(final Function transformer) { - return rebuildTree(transformer, transformer); - } - - /** - * Perform an action on each part of the tree. - * - * @param linearizationMethod - * The way to traverse the tree. - * - * @param action - * The action to perform on each tree node. - */ - void traverse(TreeLinearizationMethod linearizationMethod, Consumer action); - - /** - * Find the farthest to right child that satisfies the given predicate. - * - * @param childPred - * The predicate to satisfy. - * - * @return The index of the right-most child that satisfies the - * predicate, or -1 if one doesn't exist. - */ - int revFind(Predicate> childPred); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/Identity.java b/BJC-Utils2/src/main/java/bjc/utils/data/Identity.java deleted file mode 100644 index a8c8d70..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/Identity.java +++ /dev/null @@ -1,118 +0,0 @@ -package bjc.utils.data; - -import java.util.function.Function; -import java.util.function.UnaryOperator; - -/** - * @author ben - * - * @param - */ -/** - * Simple implementation of IHolder that has no hidden behavior - * - * @author ben - * - * @param - * The type contained in the holder - */ -public class Identity implements IHolder { - private ContainedType heldValue; - - /** - * Create a holder holding null - */ - public Identity() { - heldValue = null; - } - - /** - * Create a holder holding the specified value - * - * @param value - * The value to hold - */ - public Identity(final ContainedType value) { - heldValue = value; - } - - @Override - public IHolder bind(final Function> binder) { - return binder.apply(heldValue); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + (heldValue == null ? 0 : heldValue.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof Identity)) return false; - - final Identity other = (Identity) obj; - - if (heldValue == null) { - if (other.heldValue != null) return false; - } else if (!heldValue.equals(other.heldValue)) return false; - - return true; - } - - @Override - public Function> lift(final Function func) { - return (val) -> { - return new Identity<>(func.apply(val)); - }; - } - - @Override - public IHolder map(final Function mapper) { - return new Identity<>(mapper.apply(heldValue)); - } - - @Override - public String toString() { - return String.format("Identity [heldValue=%s]", heldValue); - } - - @Override - public IHolder transform(final UnaryOperator transformer) { - heldValue = transformer.apply(heldValue); - - return this; - } - - @Override - public UnwrappedType unwrap(final Function unwrapper) { - return unwrapper.apply(heldValue); - } - - /** - * Create a new identity container. - * - * @param val - * The contained value. - * - * @return A new identity container. - */ - public static Identity id(final ContainedType val) { - return new Identity<>(val); - } - - /** - * Create a new empty identity container. - * - * @return A new empty identity container. - */ - public static Identity id() { - return new Identity<>(); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/Lazy.java b/BJC-Utils2/src/main/java/bjc/utils/data/Lazy.java deleted file mode 100644 index ca41b62..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/Lazy.java +++ /dev/null @@ -1,194 +0,0 @@ -package bjc.utils.data; - -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.function.UnaryOperator; - -import bjc.utils.data.internals.BoundLazy; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * A holder that holds a means to create a value, but doesn't actually compute - * the value until it's needed - * - * @author ben - * - * @param - */ -public class Lazy implements IHolder { - private Supplier valueSupplier; - - private IList> actions = new FunctionalList<>(); - - private boolean valueMaterialized; - - private ContainedType heldValue; - - /** - * Create a new lazy value from the specified seed value - * - * @param value - * The seed value to use - */ - public Lazy(final ContainedType value) { - heldValue = value; - - valueMaterialized = true; - } - - /** - * Create a new lazy value from the specified value source - * - * @param supp - * The source of a value to use - */ - public Lazy(final Supplier supp) { - valueSupplier = new SingleSupplier<>(supp); - - valueMaterialized = false; - } - - private Lazy(final Supplier supp, final IList> pendingActions) { - valueSupplier = supp; - - actions = pendingActions; - } - - @Override - public IHolder bind(final Function> binder) { - final IList> pendingActions = new FunctionalList<>(); - - actions.forEach(pendingActions::add); - - final Supplier supplier = () -> { - if (valueMaterialized) return heldValue; - - return valueSupplier.get(); - }; - - return new BoundLazy<>(() -> { - return new Lazy<>(supplier, pendingActions); - }, binder); - } - - @Override - public Function> lift(final Function func) { - return val -> { - return new Lazy<>(func.apply(val)); - }; - } - - @Override - public IHolder map(final Function mapper) { - final IList> pendingActions = new FunctionalList<>(); - - actions.forEach(pendingActions::add); - - return new Lazy<>(() -> { - ContainedType currVal = heldValue; - - if (!valueMaterialized) { - currVal = valueSupplier.get(); - } - - return pendingActions.reduceAux(currVal, UnaryOperator::apply, - value -> mapper.apply(value)); - }); - } - - @Override - public String toString() { - if (valueMaterialized) { - if (actions.isEmpty()) - return String.format("value[v='%s']", heldValue); - else return String.format("value[v='%s'] (has pending transforms)", heldValue); - } - - return "(unmaterialized)"; - } - - @Override - public IHolder transform(final UnaryOperator transformer) { - actions.add(transformer); - - return this; - } - - @Override - public UnwrappedType unwrap(final Function unwrapper) { - if (!valueMaterialized) { - heldValue = valueSupplier.get(); - - valueMaterialized = true; - } - - actions.forEach(action -> { - heldValue = action.apply(heldValue); - }); - - actions = new FunctionalList<>(); - - return unwrapper.apply(heldValue); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + (actions == null ? 0 : actions.hashCode()); - result = prime * result + (heldValue == null ? 0 : heldValue.hashCode()); - result = prime * result + (valueMaterialized ? 1231 : 1237); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof Lazy)) return false; - - final Lazy other = (Lazy) obj; - - if (valueMaterialized != other.valueMaterialized) return false; - - if (valueMaterialized) { - if (heldValue == null) { - if (other.heldValue != null) return false; - } else if (!heldValue.equals(other.heldValue)) return false; - } else return false; - - if (actions == null) { - if (other.actions != null) return false; - } else if (actions.getSize() > 0 || other.actions.getSize() > 0) return false; - - return true; - } - - /** - * Create a new lazy container with an already present value. - * - * @param val - * The value for the lazy container. - * - * @return A new lazy container holding that value. - */ - public static Lazy lazy(final ContainedType val) { - return new Lazy<>(val); - } - - /** - * Create a new lazy container with a suspended value. - * - * @param supp - * The suspended value for the lazy container. - * - * @return A new lazy container that will un-suspend the value when - * necessary. - */ - public static Lazy lazy(final Supplier supp) { - return new Lazy<>(supp); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/LazyPair.java b/BJC-Utils2/src/main/java/bjc/utils/data/LazyPair.java deleted file mode 100644 index 5cb85f3..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/LazyPair.java +++ /dev/null @@ -1,240 +0,0 @@ -package bjc.utils.data; - -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; - -import bjc.utils.data.internals.BoundLazyPair; -import bjc.utils.data.internals.HalfBoundLazyPair; - -/** - * A lazy implementation of a pair - * - * @author ben - * - * @param - * The type on the left side of the pair - * @param - * The type on the right side of the pair - * - */ -public class LazyPair implements IPair { - private LeftType leftValue; - private RightType rightValue; - - private Supplier leftSupplier; - private Supplier rightSupplier; - - private boolean leftMaterialized; - private boolean rightMaterialized; - - /** - * Create a new lazy pair, using the set values - * - * @param leftVal - * The value for the left side of the pair - * @param rightVal - * The value for the right side of the pair - */ - public LazyPair(final LeftType leftVal, final RightType rightVal) { - leftValue = leftVal; - rightValue = rightVal; - - leftMaterialized = true; - rightMaterialized = true; - } - - /** - * Create a new lazy pair from the given value sources - * - * @param leftSupp - * The source for a value on the left side of the pair - * @param rightSupp - * The source for a value on the right side of the pair - */ - public LazyPair(final Supplier leftSupp, final Supplier rightSupp) { - // Use single suppliers to catch double-instantiation bugs - leftSupplier = new SingleSupplier<>(leftSupp); - rightSupplier = new SingleSupplier<>(rightSupp); - - leftMaterialized = false; - rightMaterialized = false; - } - - @Override - public IPair bind( - final BiFunction> binder) { - return new BoundLazyPair<>(leftSupplier, rightSupplier, binder); - } - - @Override - public IPair bindLeft( - final Function> leftBinder) { - final Supplier leftSupp = () -> { - if (leftMaterialized) return leftValue; - - return leftSupplier.get(); - }; - - return new HalfBoundLazyPair<>(leftSupp, leftBinder); - } - - @Override - public IPair bindRight( - final Function> rightBinder) { - final Supplier rightSupp = () -> { - if (rightMaterialized) return rightValue; - - return rightSupplier.get(); - }; - - return new HalfBoundLazyPair<>(rightSupp, rightBinder); - } - - @Override - public IPair combine( - final IPair otherPair, - final BiFunction leftCombiner, - final BiFunction rightCombiner) { - return otherPair.bind((otherLeft, otherRight) -> { - return bind((leftVal, rightVal) -> { - final CombinedLeft left = leftCombiner.apply(leftVal, otherLeft); - final CombinedRight right = rightCombiner.apply(rightVal, otherRight); - - return new LazyPair<>(left, right); - }); - }); - } - - @Override - public LeftType getLeft() { - if (!leftMaterialized) { - leftValue = leftSupplier.get(); - - leftMaterialized = true; - } - - return leftValue; - } - - @Override - public RightType getRight() { - if (!rightMaterialized) { - rightValue = rightSupplier.get(); - - rightMaterialized = true; - } - - return rightValue; - } - - @Override - public IPair mapLeft(final Function mapper) { - final Supplier leftSupp = () -> { - if (leftMaterialized) return mapper.apply(leftValue); - - return mapper.apply(leftSupplier.get()); - }; - - final Supplier rightSupp = () -> { - if (rightMaterialized) return rightValue; - - return rightSupplier.get(); - }; - - return new LazyPair<>(leftSupp, rightSupp); - } - - @Override - public IPair mapRight(final Function mapper) { - final Supplier leftSupp = () -> { - if (leftMaterialized) return leftValue; - - return leftSupplier.get(); - }; - - final Supplier rightSupp = () -> { - if (rightMaterialized) return mapper.apply(rightValue); - - return mapper.apply(rightSupplier.get()); - }; - - return new LazyPair<>(leftSupp, rightSupp); - } - - @Override - public MergedType merge(final BiFunction merger) { - if (!leftMaterialized) { - leftValue = leftSupplier.get(); - - leftMaterialized = true; - } - - if (!rightMaterialized) { - rightValue = rightSupplier.get(); - - rightMaterialized = true; - } - - return merger.apply(leftValue, rightValue); - } - - @Override - public String toString() { - String leftVal; - String rightVal; - - if (leftMaterialized) { - leftVal = leftValue.toString(); - } else { - leftVal = "(un-materialized)"; - } - - if (rightMaterialized) { - rightVal = rightValue.toString(); - } else { - rightVal = "(un-materialized)"; - } - - return String.format("pair[l=%s,r=%s]", leftVal, rightVal); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + (leftMaterialized ? 1231 : 1237); - result = prime * result + (leftValue == null ? 0 : leftValue.hashCode()); - result = prime * result + (rightMaterialized ? 1231 : 1237); - result = prime * result + (rightValue == null ? 0 : rightValue.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof LazyPair)) return false; - - final LazyPair other = (LazyPair) obj; - - if (leftMaterialized != other.leftMaterialized) return false; - - if (leftMaterialized) { - if (leftValue == null) { - if (other.leftValue != null) return false; - } else if (!leftValue.equals(other.leftValue)) return false; - } else return false; - - if (rightMaterialized != other.rightMaterialized) return false; - if (rightMaterialized) { - if (rightValue == null) { - if (other.rightValue != null) return false; - } else if (!rightValue.equals(other.rightValue)) return false; - } else return false; - - return true; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/ListHolder.java b/BJC-Utils2/src/main/java/bjc/utils/data/ListHolder.java deleted file mode 100644 index 142057c..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/ListHolder.java +++ /dev/null @@ -1,104 +0,0 @@ -package bjc.utils.data; - -import java.util.function.Function; -import java.util.function.UnaryOperator; - -import bjc.utils.data.internals.BoundListHolder; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * A holder that represents a set of non-deterministic computations - * - * @author ben - * - * @param - * The type of contained value - */ -public class ListHolder implements IHolder { - private IList heldValues; - - /** - * Create a new list holder - * - * @param values - * The possible values for the computation - */ - @SafeVarargs - public ListHolder(final ContainedType... values) { - heldValues = new FunctionalList<>(); - - if (values != null) { - for (final ContainedType containedValue : values) { - heldValues.add(containedValue); - } - } - } - - private ListHolder(final IList toHold) { - heldValues = toHold; - } - - @Override - public IHolder bind(final Function> binder) { - final IList> boundValues = heldValues.map(binder); - - return new BoundListHolder<>(boundValues); - } - - @Override - public Function> lift(final Function func) { - return val -> { - return new ListHolder<>(new FunctionalList<>(func.apply(val))); - }; - } - - @Override - public IHolder map(final Function mapper) { - final IList mappedValues = heldValues.map(mapper); - - return new ListHolder<>(mappedValues); - } - - @Override - public IHolder transform(final UnaryOperator transformer) { - heldValues = heldValues.map(transformer); - - return this; - } - - @Override - public UnwrappedType unwrap(final Function unwrapper) { - return unwrapper.apply(heldValues.randItem()); - } - - @Override - public String toString() { - return String.format("ListHolder [heldValues=%s]", heldValues); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + (heldValues == null ? 0 : heldValues.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof ListHolder)) return false; - - final ListHolder other = (ListHolder) obj; - - if (heldValues == null) { - if (other.heldValues != null) return false; - } else if (!heldValues.equals(other.heldValues)) return false; - - return true; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/Option.java b/BJC-Utils2/src/main/java/bjc/utils/data/Option.java deleted file mode 100644 index 37e0cde..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/Option.java +++ /dev/null @@ -1,93 +0,0 @@ -package bjc.utils.data; - -import java.util.function.Function; -import java.util.function.UnaryOperator; - -/** - * A holder that may or may not contain a value - * - * @author ben - * - * @param - * The type of the value that may or may not be held - */ -public class Option implements IHolder { - private ContainedType held; - - /** - * Create a new optional, using the given initial value - * - * @param seed - * The initial value for the optional - */ - public Option(final ContainedType seed) { - held = seed; - } - - @Override - public IHolder bind(final Function> binder) { - if (held == null) return new Option<>(null); - - return binder.apply(held); - } - - @Override - public Function> lift(final Function func) { - return val -> { - return new Option<>(func.apply(val)); - }; - } - - @Override - public IHolder map(final Function mapper) { - if (held == null) return new Option<>(null); - - return new Option<>(mapper.apply(held)); - } - - @Override - public IHolder transform(final UnaryOperator transformer) { - if (held != null) { - held = transformer.apply(held); - } - - return this; - } - - @Override - public UnwrappedType unwrap(final Function unwrapper) { - if (held == null) return null; - - return unwrapper.apply(held); - } - - @Override - public String toString() { - return String.format("Option [held='%s']", held); - } - - @Override - public int hashCode() { - final int prime = 31; - - int result = 1; - result = prime * result + (held == null ? 0 : held.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof Option)) return false; - - final Option other = (Option) obj; - - if (held == null) { - if (other.held != null) return false; - } else if (!held.equals(other.held)) return false; - - return true; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/Pair.java b/BJC-Utils2/src/main/java/bjc/utils/data/Pair.java deleted file mode 100644 index e6796ba..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/Pair.java +++ /dev/null @@ -1,135 +0,0 @@ -package bjc.utils.data; - -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * A pair of values, with nothing special about them. - * - * @author ben - * - * @param - * The type of the left value - * @param - * The type of the right value - */ -public class Pair implements IPair { - // The left value - private LeftType leftValue; - - // The right value - private RightType rightValue; - - /** - * Create a new pair with both sides set to null - */ - public Pair() { - - } - - /** - * Create a new pair with both sides set to the specified values - * - * @param left - * The value of the left side - * @param right - * The value of the right side - */ - public Pair(final LeftType left, final RightType right) { - leftValue = left; - rightValue = right; - } - - @Override - public IPair bind( - final BiFunction> binder) { - if (binder == null) throw new NullPointerException("Binder must not be null."); - - return binder.apply(leftValue, rightValue); - } - - @Override - public IPair bindLeft( - final Function> leftBinder) { - if (leftBinder == null) throw new NullPointerException("Binder must not be null"); - - return leftBinder.apply(leftValue); - } - - @Override - public IPair bindRight( - final Function> rightBinder) { - if (rightBinder == null) throw new NullPointerException("Binder must not be null"); - - return rightBinder.apply(rightValue); - } - - @Override - public IPair combine( - final IPair otherPair, - final BiFunction leftCombiner, - final BiFunction rightCombiner) { - return otherPair.bind((otherLeft, otherRight) -> { - final CombinedLeft left = leftCombiner.apply(leftValue, otherLeft); - final CombinedRight right = rightCombiner.apply(rightValue, otherRight); - - return new Pair<>(left, right); - }); - } - - @Override - public IPair mapLeft(final Function mapper) { - if (mapper == null) throw new NullPointerException("Mapper must not be null"); - - return new Pair<>(mapper.apply(leftValue), rightValue); - } - - @Override - public IPair mapRight(final Function mapper) { - if (mapper == null) throw new NullPointerException("Mapper must not be null"); - - return new Pair<>(leftValue, mapper.apply(rightValue)); - } - - @Override - public MergedType merge(final BiFunction merger) { - if (merger == null) throw new NullPointerException("Merger must not be null"); - - return merger.apply(leftValue, rightValue); - } - - @Override - public String toString() { - return String.format("Pair [leftValue='%s', rightValue='%s']", leftValue, rightValue); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + (leftValue == null ? 0 : leftValue.hashCode()); - result = prime * result + (rightValue == null ? 0 : rightValue.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof Pair)) return false; - - final Pair other = (Pair) obj; - - if (leftValue == null) { - if (other.leftValue != null) return false; - } else if (!leftValue.equals(other.leftValue)) return false; - - if (rightValue == null) { - if (other.rightValue != null) return false; - } else if (!rightValue.equals(other.rightValue)) return false; - - return true; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/SingleIterator.java b/BJC-Utils2/src/main/java/bjc/utils/data/SingleIterator.java deleted file mode 100644 index 4069c3f..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/SingleIterator.java +++ /dev/null @@ -1,41 +0,0 @@ -package bjc.utils.data; - -import java.util.Iterator; - -/** - * An iterator that will only ever yield one item. - * - * @author EVE - * - * @param - * The type of the item. - */ -public class SingleIterator implements Iterator { - private final T itm; - - private boolean yielded; - - /** - * Create a iterator that yields a single item. - * - * @param item - * The item to yield. - */ - public SingleIterator(final T item) { - itm = item; - - yielded = false; - } - - @Override - public boolean hasNext() { - return !yielded; - } - - @Override - public T next() { - yielded = true; - - return itm; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/SingleSupplier.java b/BJC-Utils2/src/main/java/bjc/utils/data/SingleSupplier.java deleted file mode 100644 index c675ebf..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/SingleSupplier.java +++ /dev/null @@ -1,72 +0,0 @@ -package bjc.utils.data; - -import java.util.function.Supplier; - -/** - * A supplier that can only supply one value. - * - * Attempting to retrieve another value will cause an exception to be thrown. - * - * @author ben - * - * @param - * The supplied type - */ -public class SingleSupplier implements Supplier { - private static long nextID = 0; - - private final Supplier source; - - private boolean gotten; - - private final long id; - - /* - * This is bad practice, but I want to know where the single - * instantiation was, in case of duplicate initiations. - */ - private Exception instSite; - - /** - * Create a new single supplier from an existing value - * - * @param supp - * The supplier to give a single value from - */ - public SingleSupplier(final Supplier supp) { - source = supp; - - gotten = false; - - id = nextID++; - } - - @Override - public T get() { - if (gotten == true) { - final String msg = String.format( - "Attempted to retrieve value more than once from single supplier #%d", id); - - final IllegalStateException isex = new IllegalStateException(msg); - - isex.initCause(instSite); - - throw isex; - } - - gotten = true; - - try { - throw new IllegalStateException("Previous instantiation here."); - } catch (final IllegalStateException isex) { - instSite = isex; - } - - return source.get(); - } - - @Override - public String toString() { - return String.format("SingleSupplier [source='%s', gotten=%s, id=%s]", source, gotten, id); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/Toggle.java b/BJC-Utils2/src/main/java/bjc/utils/data/Toggle.java deleted file mode 100644 index 1e10dae..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/Toggle.java +++ /dev/null @@ -1,35 +0,0 @@ -package bjc.utils.data; - -/** - * A stateful holder that swaps between two values of the same type. - * - * @author EVE - * - * @param - * The value stored in the toggle. - */ -public interface Toggle { - /** - * Retrieve the currently-aligned value of this toggle, and swap the - * alignment. - * - * @return The previously-aligned value. - */ - E get(); - - /** - * Retrieve the currently-aligned value without altering the alignment. - * - * @return The currently-aligned value. - */ - E peek(); - - /** - * Change the alignment of the toggle. - * - * @param isLeft - * Whether the toggle should be left-aligned or not. - */ - void set(boolean isLeft); - -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/TopDownTransformIterator.java b/BJC-Utils2/src/main/java/bjc/utils/data/TopDownTransformIterator.java deleted file mode 100644 index 1b87e52..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/TopDownTransformIterator.java +++ /dev/null @@ -1,208 +0,0 @@ -package bjc.utils.data; - -import static bjc.utils.data.TopDownTransformResult.RTRANSFORM; - -import java.util.Deque; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.NoSuchElementException; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; - -/* - * FIXME something is broken in here. fix it. - */ -public class TopDownTransformIterator implements Iterator> { - private final Function picker; - private final BiFunction, Consumer>>, ITree> transform; - - private ITree preParent; - private ITree postParent; - - private final Deque> preChildren; - private final Deque> postChildren; - - private TopDownTransformIterator curChild; - - private boolean done; - private boolean initial; - - private final Deque>> toYield; - private Iterator> curYield; - - public TopDownTransformIterator(final Function pickr, - final BiFunction, Consumer>>, ITree> transfrm, - final ITree tree) { - preParent = tree; - - preChildren = new LinkedList<>(); - postChildren = new LinkedList<>(); - toYield = new LinkedList<>(); - - picker = pickr; - transform = transfrm; - - done = false; - initial = true; - } - - public void addYield(final Iterator> src) { - if (curYield != null) { - toYield.push(curYield); - } - - curYield = src; - } - - @Override - public boolean hasNext() { - return !done; - } - - public ITree flushYields(final ITree val) { - if (curYield != null) { - toYield.add(new SingleIterator<>(val)); - - if (curYield.hasNext()) - return curYield.next(); - else { - while (toYield.size() != 0 && !curYield.hasNext()) { - curYield = toYield.pop(); - } - - if (toYield.size() == 0 && !curYield.hasNext()) { - curYield = null; - return val; - } else return curYield.next(); - } - } else return val; - } - - @Override - public ITree next() { - if (done) throw new NoSuchElementException(); - - if (curYield != null) { - if (curYield.hasNext()) - return curYield.next(); - else { - while (toYield.size() != 0 && !curYield.hasNext()) { - curYield = toYield.pop(); - } - - if (toYield.size() == 0 && !curYield.hasNext()) { - curYield = null; - } else return curYield.next(); - } - } - - if (initial) { - final TopDownTransformResult res = picker.apply(preParent.getHead()); - - switch (res) { - case PASSTHROUGH: - postParent = new Tree<>(preParent.getHead()); - - if (preParent.getChildrenCount() != 0) { - for (int i = 0; i < preParent.getChildrenCount(); i++) { - preChildren.add(preParent.getChild(i)); - } - - // Return whatever the first child is - break; - } else { - done = true; - return flushYields(postParent); - } - case SKIP: - done = true; - return flushYields(preParent); - case TRANSFORM: - done = true; - return flushYields(transform.apply(preParent, this::addYield)); - case RTRANSFORM: - preParent = transform.apply(preParent, this::addYield); - return flushYields(preParent); - case PUSHDOWN: - if (preParent.getChildrenCount() != 0) { - for (int i = 0; i < preParent.getChildrenCount(); i++) { - preChildren.add(preParent.getChild(i)); - } - - // Return whatever the first child is - break; - } else { - done = true; - return flushYields(transform.apply(new Tree<>(preParent.getHead()), - this::addYield)); - } - case PULLUP: - final ITree intRes = transform.apply(preParent, this::addYield); - - postParent = new Tree<>(intRes.getHead()); - - if (intRes.getChildrenCount() != 0) { - for (int i = 0; i < intRes.getChildrenCount(); i++) { - preChildren.add(intRes.getChild(i)); - } - - // Return whatever the first child is - break; - } else { - done = true; - return flushYields(postParent); - } - default: - throw new IllegalArgumentException("Unknown result type " + res); - } - - if (res != RTRANSFORM) { - initial = false; - } - } - - if (curChild == null || !curChild.hasNext()) { - if (preChildren.size() != 0) { - curChild = new TopDownTransformIterator<>(picker, transform, preChildren.pop()); - - final ITree res = curChild.next(); - System.out.println("\t\tTRACE: adding node " + res + " to children"); - postChildren.add(res); - - return flushYields(res); - } else { - ITree res = null; - - if (postParent == null) { - res = new Tree<>(preParent.getHead()); - - System.out.println("\t\tTRACE: adding nodes " + postChildren + " to " + res); - - for (final ITree child : postChildren) { - res.addChild(child); - } - - // res = transform.apply(res, - // this::addYield); - } else { - res = postParent; - - System.out.println("\t\tTRACE: adding nodes " + postChildren + " to " + res); - for (final ITree child : postChildren) { - res.addChild(child); - } - } - - done = true; - return flushYields(res); - } - } else { - final ITree res = curChild.next(); - System.out.println("\t\tTRACE: adding node " + res + " to children"); - postChildren.add(res); - - return flushYields(res); - } - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/TopDownTransformResult.java b/BJC-Utils2/src/main/java/bjc/utils/data/TopDownTransformResult.java deleted file mode 100644 index ed41eae..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/TopDownTransformResult.java +++ /dev/null @@ -1,34 +0,0 @@ -package bjc.utils.data; - -/** - * Represents the results for doing a top-down transform of a tree - * - * @author ben - * - */ -public enum TopDownTransformResult { - /** - * Do not do anything to this node, and ignore its children - */ - SKIP, - /** - * Transform this node, and don't touch its children - */ - TRANSFORM, - /** - * Transform this node, then do a top-down transform on the result - */ - RTRANSFORM, - /** - * Ignore this node, and traverse its children - */ - PASSTHROUGH, - /** - * Traverse the nodes of this children, then transform it - */ - PUSHDOWN, - /** - * Transform this node, then traverse its children - */ - PULLUP; -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/TransformIterator.java b/BJC-Utils2/src/main/java/bjc/utils/data/TransformIterator.java deleted file mode 100644 index 50f28b1..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/TransformIterator.java +++ /dev/null @@ -1,46 +0,0 @@ -package bjc.utils.data; - -import java.util.Iterator; -import java.util.function.Function; - -/** - * An iterator that transforms values from one type to another. - * - * @author EVE - * - * @param - * The source iterator type. - * - * @param - * The destination iterator type. - */ -public class TransformIterator implements Iterator { - private final Iterator source; - - private final Function transform; - - /** - * Create a new transform iterator. - * - * @param source - * The source iterator to use. - * - * @param transform - * The transform to apply. - */ - public TransformIterator(final Iterator source, final Function transform) { - this.source = source; - this.transform = transform; - } - - @Override - public boolean hasNext() { - return source.hasNext(); - } - - @Override - public D next() { - return transform.apply(source.next()); - } - -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/Tree.java b/BJC-Utils2/src/main/java/bjc/utils/data/Tree.java deleted file mode 100644 index a52f699..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/Tree.java +++ /dev/null @@ -1,390 +0,0 @@ -package bjc.utils.data; - -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.bst.TreeLinearizationMethod; -import bjc.utils.functypes.ListFlattener; - -/** - * A node in a homogeneous tree. - * - * @author ben - * - * @param - */ -public class Tree implements ITree { - private ContainedType data; - - private IList> children; - private boolean hasChildren; - private int childCount = 0; - - private int ID; - private static int nextID = 0; - - /** - * Create a new leaf node in a tree. - * - * @param leaf - * The data to store as a leaf node. - */ - public Tree(final ContainedType leaf) { - data = leaf; - - hasChildren = false; - - ID = nextID++; - } - - /** - * Create a new tree node with the specified children. - * - * @param leaf - * The data to hold in this node. - * - * @param childrn - * A list of children for this node. - */ - public Tree(final ContainedType leaf, final IList> childrn) { - this(leaf); - - hasChildren = true; - - childCount = childrn.getSize(); - - children = childrn; - } - - /** - * Create a new tree node with the specified children. - * - * @param leaf - * The data to hold in this node. - * - * @param childrn - * A list of children for this node. - */ - @SafeVarargs - public Tree(final ContainedType leaf, final ITree... childrn) { - this(leaf); - - hasChildren = true; - - childCount = 0; - - children = new FunctionalList<>(); - - for (final ITree child : childrn) { - children.add(child); - - childCount++; - } - } - - @Override - public void addChild(final ITree child) { - if (hasChildren == false) { - hasChildren = true; - - children = new FunctionalList<>(); - } - - childCount++; - - children.add(child); - } - - @Override - public void prependChild(final ITree child) { - if (hasChildren == false) { - hasChildren = true; - - children = new FunctionalList<>(); - } - - childCount++; - - children.prepend(child); - } - - @Override - public void doForChildren(final Consumer> action) { - if (childCount > 0) { - children.forEach(action); - } - } - - @Override - public int getChildrenCount() { - return childCount; - } - - @Override - public int revFind(final Predicate> childPred) { - if (childCount == 0) - return -1; - else { - for (int i = childCount - 1; i >= 0; i--) { - if (childPred.test(getChild(i))) return i; - } - } - - return -1; - } - - @Override - public void traverse(final TreeLinearizationMethod linearizationMethod, final Consumer action) { - if (hasChildren) { - switch (linearizationMethod) { - case INORDER: - if (childCount != 2) { - final String msg = "Can only do in-order traversal for binary trees."; - - throw new IllegalArgumentException(msg); - } - - children.getByIndex(0).traverse(linearizationMethod, action); - - action.accept(data); - - children.getByIndex(1).traverse(linearizationMethod, action); - break; - case POSTORDER: - children.forEach((child) -> child.traverse(linearizationMethod, action)); - - action.accept(data); - break; - case PREORDER: - action.accept(data); - - children.forEach((child) -> child.traverse(linearizationMethod, action)); - break; - default: - break; - - } - } else { - action.accept(data); - } - } - - @Override - public ReturnedType collapse(final Function leafTransform, - final Function> nodeCollapser, - final Function resultTransformer) { - return resultTransformer.apply(internalCollapse(leafTransform, nodeCollapser)); - } - - @Override - public ITree flatMapTree(final Function> mapper) { - if (hasChildren) { - final ITree flatMappedData = mapper.apply(data); - - final IList> mappedChildren = children - .map(child -> child.flatMapTree(mapper)); - - mappedChildren.forEach(child -> flatMappedData.addChild(child)); - - return flatMappedData; - } - - return mapper.apply(data); - } - - protected NewType internalCollapse(final Function leafTransform, - final Function> nodeCollapser) { - if (hasChildren) { - final Function, NewType> nodeTransformer = nodeCollapser.apply(data); - - final IList collapsedChildren = children.map(child -> { - final NewType collapsed = child.collapse(leafTransform, nodeCollapser, - subTreeVal -> subTreeVal); - - return collapsed; - }); - - return nodeTransformer.apply(collapsedChildren); - } - - return leafTransform.apply(data); - } - - protected void internalToString(final StringBuilder builder, final int indentLevel, final boolean initial) { - for (int i = 0; i < indentLevel; i++) { - builder.append(">\t"); - } - - builder.append("Node #"); - builder.append(ID); - builder.append(": "); - builder.append(data == null ? "(null)" : data.toString()); - builder.append("\n"); - - if (hasChildren) { - children.forEach(child -> { - if (child instanceof Tree) { - final Tree kid = (Tree) child; - - kid.internalToString(builder, indentLevel + 1, false); - } else { - for (int i = 0; i < indentLevel + 1; i++) { - builder.append(">\t"); - } - - builder.append("Unknown node\n"); - } - }); - } - } - - @Override - public ITree rebuildTree(final Function leafTransformer, - final Function operatorTransformer) { - if (hasChildren) { - final IList> mappedChildren = children.map(child -> { - return child.rebuildTree(leafTransformer, operatorTransformer); - }); - - return new Tree<>(operatorTransformer.apply(data), mappedChildren); - } - - return new Tree<>(leafTransformer.apply(data)); - } - - @Override - public void selectiveTransform(final Predicate nodePicker, - final UnaryOperator transformer) { - if (hasChildren) { - children.forEach(child -> child.selectiveTransform(nodePicker, transformer)); - } else { - data = transformer.apply(data); - } - } - - @Override - public ITree topDownTransform( - final Function transformPicker, - final UnaryOperator> transformer) { - final TopDownTransformResult transformResult = transformPicker.apply(data); - - switch (transformResult) { - case PASSTHROUGH: - ITree result = new Tree<>(data); - - if (hasChildren) { - children.forEach(child -> { - final ITree kid = child.topDownTransform(transformPicker, - transformer); - - result.addChild(kid); - }); - } - - return result; - case SKIP: - return this; - case TRANSFORM: - return transformer.apply(this); - case RTRANSFORM: - return transformer.apply(this).topDownTransform(transformPicker, transformer); - case PUSHDOWN: - result = new Tree<>(data); - - if (hasChildren) { - children.forEach(child -> { - final ITree kid = child.topDownTransform(transformPicker, - transformer); - - result.addChild(kid); - }); - } - - return transformer.apply(result); - case PULLUP: - final ITree intermediateResult = transformer.apply(this); - - result = new Tree<>(intermediateResult.getHead()); - - intermediateResult.doForChildren(child -> { - final ITree kid = child.topDownTransform(transformPicker, transformer); - - result.addChild(kid); - }); - - return result; - default: - final String msg = String.format("Recieved unknown transform result %s", transformResult); - - throw new IllegalArgumentException(msg); - } - } - - @Override - public TransformedType transformChild(final int childNo, - final Function, TransformedType> transformer) { - if (childNo < 0 || childNo > childCount - 1) { - final String msg = String.format("Child index #%d is invalid", childNo); - - throw new IllegalArgumentException(msg); - } - - final ITree selectedKid = children.getByIndex(childNo); - - return transformer.apply(selectedKid); - } - - @Override - public TransformedType transformHead( - final Function transformer) { - return transformer.apply(data); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + childCount; - result = prime * result + (children == null ? 0 : children.hashCode()); - result = prime * result + (data == null ? 0 : data.hashCode()); - - return result; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - - internalToString(builder, 1, true); - - builder.deleteCharAt(builder.length() - 1); - - return builder.toString(); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof Tree)) return false; - - final Tree other = (Tree) obj; - - if (data == null) { - if (other.data != null) return false; - } else if (!data.equals(other.data)) return false; - - if (childCount != other.childCount) return false; - - if (children == null) { - if (other.children != null) return false; - } else if (!children.equals(other.children)) return false; - - return true; - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/ValueToggle.java b/BJC-Utils2/src/main/java/bjc/utils/data/ValueToggle.java deleted file mode 100644 index 9193896..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/ValueToggle.java +++ /dev/null @@ -1,54 +0,0 @@ -package bjc.utils.data; - -/** - * A simple implementation of {@link Toggle}. - * - * @author EVE - * - * @param - * The type of value to toggle between. - */ -public class ValueToggle implements Toggle { - private final E lft; - private final E rght; - - private final BooleanToggle alignment; - - /** - * Create a new toggle. - * - * All toggles start right-aligned. - * - * @param left - * The value when the toggle is left-aligned. - * - * @param right - * The value when the toggle is right-aligned. - */ - public ValueToggle(final E left, final E right) { - lft = left; - - rght = right; - - alignment = new BooleanToggle(); - } - - @Override - public E get() { - if (alignment.get()) - return lft; - else return rght; - } - - @Override - public E peek() { - if (alignment.peek()) - return lft; - else return rght; - } - - @Override - public void set(final boolean isLeft) { - alignment.set(isLeft); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundLazy.java b/BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundLazy.java deleted file mode 100644 index f71d32b..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundLazy.java +++ /dev/null @@ -1,145 +0,0 @@ -package bjc.utils.data.internals; - -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.function.UnaryOperator; - -import bjc.utils.data.IHolder; -import bjc.utils.data.Lazy; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/* - * Implements a lazy holder that has been bound - */ -public class BoundLazy implements IHolder { - /* - * The old value - */ - private final Supplier> oldSupplier; - - /* - * The function to use to transform the old value into a new value - */ - private final Function> binder; - - /* - * The bound value being held - */ - private IHolder boundHolder; - - /* - * Whether the bound value has been actualized or not - */ - private boolean holderBound; - - /* - * Transformations currently pending on the bound value - */ - private final IList> actions = new FunctionalList<>(); - - /* - * Create a new bound lazy value - */ - public BoundLazy(final Supplier> supp, - final Function> binder) { - oldSupplier = supp; - this.binder = binder; - } - - @Override - public IHolder bind(final Function> bindr) { - if (bindr == null) throw new NullPointerException("Binder must not be null"); - - /* - * Prepare a list of pending actions - */ - final IList> pendingActions = new FunctionalList<>(); - actions.forEach(pendingActions::add); - - /* - * Create the new supplier of a value - */ - final Supplier> typeSupplier = () -> { - IHolder oldHolder = boundHolder; - - /* - * Bind the value if it hasn't been bound before - */ - if (!holderBound) { - oldHolder = oldSupplier.get().unwrap(binder); - } - - /* - * Apply all the pending actions - */ - return pendingActions.reduceAux(oldHolder, (action, state) -> { - return state.transform(action); - }, (value) -> value); - }; - - return new BoundLazy<>(typeSupplier, bindr); - } - - @Override - public Function> lift( - final Function func) { - if (func == null) throw new NullPointerException("Function to lift must not be null"); - - return (val) -> { - return new Lazy<>(func.apply(val)); - }; - } - - @Override - public IHolder map(final Function mapper) { - if (mapper == null) throw new NullPointerException("Mapper must not be null"); - - // Prepare a list of pending actions - final IList> pendingActions = new FunctionalList<>(); - actions.forEach(pendingActions::add); - - // Prepare the new supplier - final Supplier typeSupplier = () -> { - IHolder oldHolder = boundHolder; - - // Bound the value if it hasn't been bound - if (!holderBound) { - oldHolder = oldSupplier.get().unwrap(binder); - } - - return pendingActions.reduceAux(oldHolder.getValue(), (action, state) -> { - return action.apply(state); - }, (value) -> mapper.apply(value)); - }; - - return new Lazy<>(typeSupplier); - } - - @Override - public String toString() { - if (holderBound) return boundHolder.toString(); - - return "(unmaterialized)"; - } - - @Override - public IHolder transform(final UnaryOperator transformer) { - if (transformer == null) throw new NullPointerException("Transformer must not be null"); - - actions.add(transformer); - - return this; - } - - @Override - public UnwrappedType unwrap(final Function unwrapper) { - if (unwrapper == null) throw new NullPointerException("Unwrapper must not be null"); - - if (!holderBound) { - boundHolder = oldSupplier.get().unwrap(binder::apply); - } - - return boundHolder.unwrap(unwrapper); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundLazyPair.java b/BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundLazyPair.java deleted file mode 100644 index df6e60b..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundLazyPair.java +++ /dev/null @@ -1,199 +0,0 @@ -package bjc.utils.data.internals; - -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; - -import bjc.utils.data.IHolder; -import bjc.utils.data.IPair; -import bjc.utils.data.Identity; -import bjc.utils.data.LazyPair; - -/* - * Implements a lazy pair that has been bound - */ -public class BoundLazyPair implements IPair { - /* - * The supplier of the left value - */ - private final Supplier leftSupplier; - /* - * The supplier of the right value - */ - private final Supplier rightSupplier; - - /* - * The binder to transform values - */ - private final BiFunction> binder; - - /* - * The bound pair - */ - private IPair boundPair; - - /* - * Whether the pair has been bound yet - */ - private boolean pairBound; - - public BoundLazyPair(final Supplier leftSupp, final Supplier rightSupp, - final BiFunction> bindr) { - leftSupplier = leftSupp; - rightSupplier = rightSupp; - binder = bindr; - } - - @Override - public IPair bind( - final BiFunction> bindr) { - if (bindr == null) throw new NullPointerException("Binder must not be null"); - - final IHolder> newPair = new Identity<>(boundPair); - final IHolder newPairMade = new Identity<>(pairBound); - - final Supplier leftSupp = () -> { - if (!newPairMade.getValue()) { - newPair.replace(binder.apply(leftSupplier.get(), rightSupplier.get())); - - newPairMade.replace(true); - } - - return newPair.unwrap((pair) -> pair.getLeft()); - }; - - final Supplier rightSupp = () -> { - if (!newPairMade.getValue()) { - newPair.replace(binder.apply(leftSupplier.get(), rightSupplier.get())); - - newPairMade.replace(true); - } - - return newPair.unwrap((pair) -> pair.getRight()); - }; - - return new BoundLazyPair<>(leftSupp, rightSupp, bindr); - } - - @Override - public IPair bindLeft( - final Function> leftBinder) { - if (leftBinder == null) throw new NullPointerException("Left binder must not be null"); - - final Supplier leftSupp = () -> { - IPair newPair = boundPair; - - if (!pairBound) { - newPair = binder.apply(leftSupplier.get(), rightSupplier.get()); - } - - return newPair.getLeft(); - }; - - return new HalfBoundLazyPair<>(leftSupp, leftBinder); - } - - @Override - public IPair bindRight( - final Function> rightBinder) { - if (rightBinder == null) throw new NullPointerException("Right binder must not be null"); - - final Supplier rightSupp = () -> { - IPair newPair = boundPair; - - if (!pairBound) { - newPair = binder.apply(leftSupplier.get(), rightSupplier.get()); - } - - return newPair.getRight(); - }; - - return new HalfBoundLazyPair<>(rightSupp, rightBinder); - } - - @Override - public IPair combine( - final IPair otherPair, - final BiFunction leftCombiner, - final BiFunction rightCombiner) { - if (otherPair == null) - throw new NullPointerException("Other pair must not be null"); - else if (leftCombiner == null) - throw new NullPointerException("Left combiner must not be null"); - else if (rightCombiner == null) throw new NullPointerException("Right combiner must not be null"); - - return otherPair.bind((otherLeft, otherRight) -> { - return bind((leftVal, rightVal) -> { - return new LazyPair<>(leftCombiner.apply(leftVal, otherLeft), - rightCombiner.apply(rightVal, otherRight)); - }); - }); - } - - @Override - public IPair mapLeft(final Function mapper) { - if (mapper == null) throw new NullPointerException("Mapper must not be null"); - - final Supplier leftSupp = () -> { - if (!pairBound) { - final NewLeft leftVal = binder.apply(leftSupplier.get(), rightSupplier.get()).getLeft(); - - return mapper.apply(leftVal); - } - - return mapper.apply(boundPair.getLeft()); - }; - - final Supplier rightSupp = () -> { - if (!pairBound) return binder.apply(leftSupplier.get(), rightSupplier.get()).getRight(); - - return boundPair.getRight(); - }; - - return new LazyPair<>(leftSupp, rightSupp); - } - - @Override - public IPair mapRight(final Function mapper) { - if (mapper == null) throw new NullPointerException("Mapper must not be null"); - - final Supplier leftSupp = () -> { - if (!pairBound) return binder.apply(leftSupplier.get(), rightSupplier.get()).getLeft(); - - return boundPair.getLeft(); - }; - - final Supplier rightSupp = () -> { - if (!pairBound) { - final NewRight rightVal = binder.apply(leftSupplier.get(), rightSupplier.get()) - .getRight(); - - return mapper.apply(rightVal); - } - - return mapper.apply(boundPair.getRight()); - }; - - return new LazyPair<>(leftSupp, rightSupp); - } - - @Override - public MergedType merge(final BiFunction merger) { - if (merger == null) throw new NullPointerException("Merger must not be null"); - - if (!pairBound) { - boundPair = binder.apply(leftSupplier.get(), rightSupplier.get()); - - pairBound = true; - } - - return boundPair.merge(merger); - } - - @Override - public String toString() { - if (pairBound) return boundPair.toString(); - - return "(un-materialized)"; - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundListHolder.java b/BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundListHolder.java deleted file mode 100644 index f3799fd..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/internals/BoundListHolder.java +++ /dev/null @@ -1,68 +0,0 @@ -package bjc.utils.data.internals; - -import java.util.function.Function; -import java.util.function.UnaryOperator; - -import bjc.utils.data.IHolder; -import bjc.utils.data.ListHolder; -import bjc.utils.funcdata.IList; - -/* - * Holds a list, converted into a holder - */ -public class BoundListHolder implements IHolder { - private final IList> heldHolders; - - public BoundListHolder(final IList> toHold) { - heldHolders = toHold; - } - - @Override - public IHolder bind(final Function> binder) { - if (binder == null) throw new NullPointerException("Binder must not be null"); - - final IList> boundHolders = heldHolders.map((containedHolder) -> { - return containedHolder.bind(binder); - }); - - return new BoundListHolder<>(boundHolders); - } - - @Override - public Function> lift(final Function func) { - if (func == null) throw new NullPointerException("Function to lift must not be null"); - - return (val) -> { - return new ListHolder<>(func.apply(val)); - }; - } - - @Override - public IHolder map(final Function mapper) { - if (mapper == null) throw new NullPointerException("Mapper must not be null"); - - final IList> mappedHolders = heldHolders.map((containedHolder) -> { - return containedHolder.map(mapper); - }); - - return new BoundListHolder<>(mappedHolders); - } - - @Override - public IHolder transform(final UnaryOperator transformer) { - if (transformer == null) throw new NullPointerException("Transformer must not be null"); - - heldHolders.forEach((containedHolder) -> { - containedHolder.transform(transformer); - }); - - return this; - } - - @Override - public UnwrappedType unwrap(final Function unwrapper) { - if (unwrapper == null) throw new NullPointerException("Unwrapper must not be null"); - - return heldHolders.randItem().unwrap(unwrapper); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/internals/HalfBoundLazyPair.java b/BJC-Utils2/src/main/java/bjc/utils/data/internals/HalfBoundLazyPair.java deleted file mode 100644 index 8cac38b..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/internals/HalfBoundLazyPair.java +++ /dev/null @@ -1,149 +0,0 @@ -package bjc.utils.data.internals; - -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; - -import bjc.utils.data.IHolder; -import bjc.utils.data.IPair; -import bjc.utils.data.Identity; -import bjc.utils.data.LazyPair; - -/* - * A lazy pair, with only one side bound - */ -public class HalfBoundLazyPair implements IPair { - private final Supplier oldSupplier; - - private final Function> binder; - - private IPair boundPair; - private boolean pairBound; - - public HalfBoundLazyPair(final Supplier oldSupp, - final Function> bindr) { - oldSupplier = oldSupp; - binder = bindr; - } - - @Override - public IPair bind( - final BiFunction> bindr) { - final IHolder> newPair = new Identity<>(boundPair); - final IHolder newPairMade = new Identity<>(pairBound); - - final Supplier leftSupp = () -> { - if (!newPairMade.getValue()) { - newPair.replace(binder.apply(oldSupplier.get())); - newPairMade.replace(true); - } - - return newPair.unwrap((pair) -> pair.getLeft()); - }; - - final Supplier rightSupp = () -> { - if (!newPairMade.getValue()) { - newPair.replace(binder.apply(oldSupplier.get())); - newPairMade.replace(true); - } - - return newPair.unwrap((pair) -> pair.getRight()); - }; - - return new BoundLazyPair<>(leftSupp, rightSupp, bindr); - } - - @Override - public IPair bindLeft( - final Function> leftBinder) { - final Supplier leftSupp = () -> { - IPair newPair = boundPair; - - if (!pairBound) { - newPair = binder.apply(oldSupplier.get()); - } - - return newPair.getLeft(); - }; - - return new HalfBoundLazyPair<>(leftSupp, leftBinder); - } - - @Override - public IPair bindRight( - final Function> rightBinder) { - final Supplier rightSupp = () -> { - IPair newPair = boundPair; - - if (!pairBound) { - newPair = binder.apply(oldSupplier.get()); - } - - return newPair.getRight(); - }; - - return new HalfBoundLazyPair<>(rightSupp, rightBinder); - } - - @Override - public IPair combine( - final IPair otherPair, - final BiFunction leftCombiner, - final BiFunction rightCombiner) { - return otherPair.bind((otherLeft, otherRight) -> { - return bind((leftVal, rightVal) -> { - return new LazyPair<>(leftCombiner.apply(leftVal, otherLeft), - rightCombiner.apply(rightVal, otherRight)); - }); - }); - } - - @Override - public IPair mapLeft(final Function mapper) { - final Supplier leftSupp = () -> { - if (pairBound) return mapper.apply(boundPair.getLeft()); - - final NewLeft leftVal = binder.apply(oldSupplier.get()).getLeft(); - - return mapper.apply(leftVal); - }; - - final Supplier rightSupp = () -> { - if (pairBound) return boundPair.getRight(); - - return binder.apply(oldSupplier.get()).getRight(); - }; - - return new LazyPair<>(leftSupp, rightSupp); - } - - @Override - public IPair mapRight(final Function mapper) { - final Supplier leftSupp = () -> { - if (pairBound) return boundPair.getLeft(); - - return binder.apply(oldSupplier.get()).getLeft(); - }; - - final Supplier rightSupp = () -> { - if (pairBound) return mapper.apply(boundPair.getRight()); - - final NewRight rightVal = binder.apply(oldSupplier.get()).getRight(); - - return mapper.apply(rightVal); - }; - - return new LazyPair<>(leftSupp, rightSupp); - } - - @Override - public MergedType merge(final BiFunction merger) { - if (!pairBound) { - boundPair = binder.apply(oldSupplier.get()); - - pairBound = true; - } - - return boundPair.merge(merger); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/internals/WrappedLazy.java b/BJC-Utils2/src/main/java/bjc/utils/data/internals/WrappedLazy.java deleted file mode 100644 index 4175724..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/internals/WrappedLazy.java +++ /dev/null @@ -1,62 +0,0 @@ -package bjc.utils.data.internals; - -import java.util.function.Function; -import java.util.function.UnaryOperator; - -import bjc.utils.data.IHolder; -import bjc.utils.data.Lazy; - -public class WrappedLazy implements IHolder { - private final IHolder> held; - - public WrappedLazy(final IHolder wrappedHolder) { - held = new Lazy<>(wrappedHolder); - } - - // This has an extra parameter, because otherwise it erases to the same - // as the public one - private WrappedLazy(final IHolder> wrappedHolder, final boolean dummy) { - held = wrappedHolder; - } - - @Override - public IHolder bind(final Function> binder) { - final IHolder> newHolder = held.map((containedHolder) -> { - return containedHolder.bind(binder); - }); - - return new WrappedLazy<>(newHolder, false); - } - - @Override - public Function> lift(final Function func) { - return (val) -> { - return new Lazy<>(func.apply(val)); - }; - } - - @Override - public IHolder map(final Function mapper) { - final IHolder> newHolder = held.map((containedHolder) -> { - return containedHolder.map(mapper); - }); - - return new WrappedLazy<>(newHolder, false); - } - - @Override - public IHolder transform(final UnaryOperator transformer) { - held.transform((containedHolder) -> { - return containedHolder.transform(transformer); - }); - - return this; - } - - @Override - public UnwrappedType unwrap(final Function unwrapper) { - return held.unwrap((containedHolder) -> { - return containedHolder.unwrap(unwrapper); - }); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/internals/WrappedOption.java b/BJC-Utils2/src/main/java/bjc/utils/data/internals/WrappedOption.java deleted file mode 100644 index 512c699..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/data/internals/WrappedOption.java +++ /dev/null @@ -1,76 +0,0 @@ -package bjc.utils.data.internals; - -import java.util.function.Function; -import java.util.function.UnaryOperator; - -import bjc.utils.data.IHolder; -import bjc.utils.data.Option; - -public class WrappedOption implements IHolder { - private final IHolder> held; - - public WrappedOption(final IHolder seedValue) { - held = new Option<>(seedValue); - } - - private WrappedOption(final IHolder> toHold, final boolean dummy) { - held = toHold; - } - - @Override - public IHolder bind(final Function> binder) { - final IHolder> newHolder = held.map((containedHolder) -> { - return containedHolder.bind((containedValue) -> { - if (containedValue == null) return new Option<>(null); - - return binder.apply(containedValue); - }); - }); - - return new WrappedOption<>(newHolder, false); - } - - @Override - public Function> lift(final Function func) { - return (val) -> { - return new Option<>(func.apply(val)); - }; - } - - @Override - public IHolder map(final Function mapper) { - final IHolder> newHolder = held.map((containedHolder) -> { - return containedHolder.map((containedValue) -> { - if (containedValue == null) return null; - - return mapper.apply(containedValue); - }); - }); - - return new WrappedOption<>(newHolder, false); - } - - @Override - public IHolder transform(final UnaryOperator transformer) { - held.transform((containedHolder) -> { - return containedHolder.transform((containedValue) -> { - if (containedValue == null) return null; - - return transformer.apply(containedValue); - }); - }); - - return this; - } - - @Override - public UnwrappedType unwrap(final Function unwrapper) { - return held.unwrap((containedHolder) -> { - return containedHolder.unwrap((containedValue) -> { - if (containedValue == null) return null; - - return unwrapper.apply(containedValue); - }); - }); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/AbbrevMap.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/AbbrevMap.java deleted file mode 100644 index 0d54471..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/AbbrevMap.java +++ /dev/null @@ -1,227 +0,0 @@ -package bjc.utils.esodata; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.SetMultimap; - -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IMap; - -/** - * Represents a mapping from a set of strings to a mapping of all unambiguous - * prefixes of their respective strings. - * - * This works the same as Ruby's Abbrev. - * - * @author EVE - * - */ -public class AbbrevMap { - /* - * All of the words we have abbreviations for. - */ - private final Set wrds; - - /* - * Maps abbreviations to their strings. - */ - private IMap abbrevMap; - - /* - * Counts how many times we've seen a substring. - */ - private Set seen; - - /* - * Maps ambiguous abbreviations to the strings they could be. - */ - private SetMultimap ambMap; - - /** - * Create a new abbreviation map. - * - * @param words - * The initial set of words to put in the map. - */ - public AbbrevMap(final String... words) { - wrds = new HashSet<>(Arrays.asList(words)); - - recalculate(); - } - - /** - * Recalculate all the abbreviations in this map. - */ - public void recalculate() { - abbrevMap = new FunctionalMap<>(); - - ambMap = HashMultimap.create(); - - seen = new HashSet<>(); - - for (final String word : wrds) { - /* - * A word always abbreviates to itself. - */ - abbrevMap.put(word, word); - - intAddWord(word); - } - } - - /** - * Adds words to the abbreviation map. - * - * @param words - * The words to add to the abbreviation map. - */ - public void addWords(final String... words) { - wrds.addAll(Arrays.asList(words)); - - for (final String word : words) { - /* - * A word always abbreviates to itself. - */ - abbrevMap.put(word, word); - - intAddWord(word); - } - } - - /* - * Actually add abbreviations of a word. - */ - private void intAddWord(final String word) { - /* - * Skip blank words. - */ - if (word.equals("")) return; - - /* - * Handle each possible abbreviation. - */ - for (int i = word.length(); i > 0; i--) { - final String subword = word.substring(0, i); - - if (seen.contains(subword)) { - /* - * Remove a mapping if its ambiguous and not a - * whole word. - */ - if (abbrevMap.containsKey(subword) && !wrds.contains(subword)) { - final String oldword = abbrevMap.remove(subword); - - ambMap.put(subword, oldword); - ambMap.put(subword, word); - } else if (!wrds.contains(subword)) { - ambMap.put(subword, word); - } - } else { - seen.add(subword); - - abbrevMap.put(subword, word); - } - } - } - - /** - * Removes words from the abbreviation map. - * - * NOTE: There may be inconsistent behavior after removing a word from - * the map. Use {@link AbbrevMap#recalculate()} to fix it if it occurs. - * - * @param words - * The words to remove. - */ - public void removeWords(final String... words) { - wrds.removeAll(Arrays.asList(words)); - - for (final String word : words) { - intRemoveWord(word); - } - } - - /* - * Actually remove a word. - */ - private void intRemoveWord(final String word) { - /* - * Skip blank words. - */ - if (word.equals("")) return; - - /* - * Handle each possible abbreviation. - */ - for (int i = word.length(); i > 0; i--) { - final String subword = word.substring(0, i); - - if (abbrevMap.containsKey(subword)) { - abbrevMap.remove(subword); - } else { - ambMap.remove(subword, word); - - final Set possWords = ambMap.get(subword); - - if (possWords.size() == 0) { - seen.remove(subword); - } else if (possWords.size() == 1) { - final String newWord = possWords.iterator().next(); - - abbrevMap.put(subword, newWord); - ambMap.remove(subword, newWord); - } - } - } - } - - /** - * Convert an abbreviation into all the strings it could abbreviate - * into. - * - * @param abbrev - * The abbreviation to convert. - * - * @return All the expansions for the provided abbreviation. - */ - public String[] deabbrev(final String abbrev) { - if (abbrevMap.containsKey(abbrev)) - return new String[] { abbrevMap.get(abbrev) }; - else return ambMap.get(abbrev).toArray(new String[0]); - } - - @Override - public int hashCode() { - final int prime = 31; - - int result = 1; - result = prime * result + (wrds == null ? 0 : wrds.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof AbbrevMap)) return false; - - final AbbrevMap other = (AbbrevMap) obj; - - if (wrds == null) { - if (other.wrds != null) return false; - } else if (!wrds.equals(other.wrds)) return false; - - return true; - } - - @Override - public String toString() { - final String fmt = "AbbrevMap [wrds=%s, abbrevMap=%s, seen=%s, ambMap=%s]"; - - return String.format(fmt, wrds, abbrevMap, seen, ambMap); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/Directory.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/Directory.java deleted file mode 100644 index 17b70f5..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/Directory.java +++ /dev/null @@ -1,106 +0,0 @@ -package bjc.utils.esodata; - -/** - * Represents a hierarchical map. - * - * What's useful about this is that you can hand sub-directories to people and - * be able to ensure that they can't write outside of it. - * - * @param - * The key type of the map. - * @param - * The value type of the map. - */ -public interface Directory { - /** - * Retrieves a given sub-directory. - * - * @param key - * The key to retrieve the sub-directory for. - * - * @return The sub-directory under that name. - * - * @throws IllegalArgumentException - * If the given sub-directory doesn't exist. - */ - Directory getSubdirectory(K key); - - /** - * Check if a given sub-directory exists. - * - * @param key - * The key to look for the sub-directory under. - * - * @return Whether or not a sub-directory of that name exists. - */ - boolean hasSubdirectory(K key); - - /** - * Insert a sub-directory into the dictionary. - * - * @param key - * The name of the new sub-directory - * @param value - * The sub-directory to insert - * - * @return The old sub-directory attached to this key, or null if such a - * sub-directory didn't exist - */ - Directory putSubdirectory(K key, Directory value); - - /** - * Create a new sub-directory. - * - * Will fail if a sub-directory of that name already exists. - * - * @param key - * The name of the new sub-directory. - * - * @return The new sub-directory, or null if one by that name already - * exists. - */ - default Directory newSubdirectory(final K key) { - if (hasSubdirectory(key)) return null; - - final Directory dir = new SimpleDirectory<>(); - - putSubdirectory(key, dir); - - return dir; - } - - /** - * Check if the directory contains a data-item under the given key. - * - * @param key - * The key to check for. - * - * @return Whether or not there is a data item for the given key. - */ - boolean containsKey(K key); - - /** - * Retrieve a given data-item from the directory. - * - * @param key - * The key to retrieve data for. - * - * @return The value for the given key. - * - * @throws IllegalArgumentException - * If no value exists for the given key. - */ - V getKey(K key); - - /** - * Insert a data-item into the directory. - * - * @param key - * The key to insert into. - * @param val - * The value to insert. - * - * @return The old value of key, or null if such a value didn't exist. - */ - V putKey(K key, V val); -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/DoubleTape.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/DoubleTape.java deleted file mode 100644 index 5c463c6..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/DoubleTape.java +++ /dev/null @@ -1,258 +0,0 @@ -package bjc.utils.esodata; - -/** - * Double-sided tape is essentially two tapes stuck together with a shared - * cursor. - * - * The main way a double-sided tape differs is that it can be flipped, allowing - * access to another set of data. - * - * However, there is only one cursor, and the position of the cursor on one side - * is the inverse of the position on the other side. - * - * When one side is extended, a null will be inserted into the inactive side - * regardless of the auto-extension policy of the tape. The policy will still be - * respected for the active side. - * - * All operations that refer to the tape refer to the currently active side of - * the tape, except for flip. - * - * Flip refers to the entire tape for 'obvious' reasons. - * - * @param - * The element type of the tape. - * @author bjculkin - */ -public class DoubleTape implements Tape { - private Tape front; - private Tape back; - - /** - * Create a new empty double-sided tape that doesn't autoextend. - */ - public DoubleTape() { - this(false); - } - - /** - * Create a new empty double-sided tape that follows the specified - * auto-extension policy. - * - * @param autoExtnd - * Whether or not to auto-extend the tape to the right w/ - * nulls. - */ - public DoubleTape(final boolean autoExtnd) { - front = new SingleTape<>(autoExtnd); - back = new SingleTape<>(autoExtnd); - } - - /** - * Get the item the tape is currently on. - * - * @return The item the tape is on. - */ - @Override - public T item() { - return front.item(); - } - - /** - * Set the item the tape is currently on. - * - * @param itm - * The new value for the tape item. - */ - @Override - public void item(final T itm) { - front.item(itm); - } - - /** - * Get the current number of elements in the tape. - * - * @return The current number of elements in the tape. - */ - @Override - public int size() { - return front.size(); - } - - @Override - public int position() { - return front.position(); - } - - /** - * Insert an element before the current item. - * - * @param itm - * The item to add. - */ - @Override - public void insertBefore(final T itm) { - front.insertBefore(itm); - back.insertAfter(null); - } - - /** - * Insert an element after the current item. - */ - @Override - public void insertAfter(final T itm) { - front.insertAfter(itm); - back.insertBefore(itm); - } - - /** - * Remove the current element. - * - * Also moves the cursor back one step if possible to maintain relative - * position, and removes the corresponding item from the non-active side - * - * @return The removed item from the active side. - */ - @Override - public T remove() { - back.remove(); - - return front.remove(); - } - - /** - * Move the cursor to the left-most position. - */ - @Override - public void first() { - front.first(); - back.last(); - } - - /** - * Move the cursor the right-most position. - */ - @Override - public void last() { - front.last(); - back.first(); - } - - /** - * Move the cursor one space left. - * - * The cursor can't go past zero. - * - * @return True if the cursor was moved left. - */ - @Override - public boolean left() { - return left(1); - } - - /** - * Move the cursor the specified amount left. - * - * The cursor can't go past zero. Attempts to move the cursor by amounts - * that would exceed zero don't move the cursor at all. - * - * @param amt - * The amount to attempt to move the cursor left. - * - * @return True if the cursor was moved left. - */ - @Override - public boolean left(final int amt) { - final boolean succ = front.left(amt); - - if (succ) { - back.right(amt); - } - - return succ; - } - - /** - * Move the cursor one space right. - * - * Moving the cursor right will auto-extend the tape if that is enabled. - * - * @return Whether the cursor was moved right. - */ - @Override - public boolean right() { - return right(1); - } - - /** - * Move the cursor the specified amount right. - * - * Moving the cursor right will auto-extend the tape if that is enabled. - * - * @param amt - * The amount to move the cursor right by. - * - * @return Whether the cursor was moved right. - */ - @Override - public boolean right(final int amt) { - final boolean succ = front.right(amt); - - if (succ) { - back.left(amt); - } - - return succ; - } - - /** - * Flips the tape. - * - * The active side becomes inactive, and the inactive side becomes - * active. - */ - public void flip() { - final Tape tmp = front; - - front = back; - - back = tmp; - } - - @Override - public boolean isDoubleSided() { - return true; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (back == null ? 0 : back.hashCode()); - result = prime * result + (front == null ? 0 : front.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof DoubleTape)) return false; - - final DoubleTape other = (DoubleTape) obj; - - if (back == null) { - if (other.back != null) return false; - } else if (!back.equals(other.back)) return false; - - if (front == null) { - if (other.front != null) return false; - } else if (!front.equals(other.front)) return false; - - return true; - } - - @Override - public String toString() { - return String.format("DoubleTape [front=%s, back=%s]", front, back); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/PushdownMap.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/PushdownMap.java deleted file mode 100644 index a631704..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/PushdownMap.java +++ /dev/null @@ -1,148 +0,0 @@ -package bjc.utils.esodata; - -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; - -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.IMap; - -/** - * A variant of a map where inserting a duplicate key shadows the existing value - * instead of replacing it. - * - * @author EVE - * - * @param - * The key of the map. - * @param - * The values in the map. - */ -public class PushdownMap implements IMap { - private final IMap> backing; - - /** - * Create a new empty stack-based map. - */ - public PushdownMap() { - backing = new FunctionalMap<>(); - } - - private PushdownMap(final IMap> back) { - backing = back; - } - - @Override - public void clear() { - backing.clear(); - } - - @Override - public boolean containsKey(final KeyType key) { - return backing.containsKey(key); - } - - @Override - public IMap extend() { - return new PushdownMap<>(backing.extend()); - } - - @Override - public void forEach(final BiConsumer action) { - backing.forEach((key, stk) -> action.accept(key, stk.top())); - } - - @Override - public void forEachKey(final Consumer action) { - backing.forEachKey(action); - } - - @Override - public void forEachValue(final Consumer action) { - backing.forEachValue(stk -> action.accept(stk.top())); - } - - @Override - public ValueType get(final KeyType key) { - return backing.get(key).top(); - } - - @Override - public int size() { - return backing.size(); - } - - @Override - public IList keyList() { - return backing.keyList(); - } - - @Override - public IMap transform(final Function transformer) { - throw new UnsupportedOperationException("Cannot transform pushdown maps."); - } - - @Override - public ValueType put(final KeyType key, final ValueType val) { - if (backing.containsKey(key)) { - final Stack stk = backing.get(key); - - final ValueType vl = stk.top(); - - stk.push(val); - - return vl; - } else { - final Stack stk = new SimpleStack<>(); - - stk.push(val); - - return null; - } - } - - @Override - public ValueType remove(final KeyType key) { - final Stack stk = backing.get(key); - - if (stk.size() > 1) - return stk.pop(); - else return backing.remove(key).top(); - } - - @Override - public IList valueList() { - return backing.valueList().map(stk -> stk.top()); - } - - @Override - public int hashCode() { - final int prime = 31; - - int result = 1; - result = prime * result + (backing == null ? 0 : backing.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof PushdownMap)) return false; - - final PushdownMap other = (PushdownMap) obj; - - if (backing == null) { - if (other.backing != null) return false; - } else if (!backing.equals(other.backing)) return false; - - return true; - } - - @Override - public String toString() { - return String.format("PushdownMap [backing=%s]", backing); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/QueueStack.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/QueueStack.java deleted file mode 100644 index 850598a..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/QueueStack.java +++ /dev/null @@ -1,88 +0,0 @@ -package bjc.utils.esodata; - -import java.util.Deque; -import java.util.LinkedList; - -/** - * A FIFO implementation of a stack. - * - * @param - * The datatype stored in the stack. - * @author Ben Culkin - */ -public class QueueStack extends Stack { - private final Deque backing; - - /** - * Create a new empty stack queue. - * - */ - public QueueStack() { - backing = new LinkedList<>(); - } - - @Override - public void push(final T elm) { - backing.add(elm); - } - - @Override - public T pop() { - if (backing.isEmpty()) throw new StackUnderflowException(); - - return backing.remove(); - } - - @Override - public T top() { - if (backing.isEmpty()) throw new StackUnderflowException(); - - return backing.peek(); - } - - @Override - public int size() { - return backing.size(); - } - - @Override - public boolean empty() { - return backing.size() == 0; - } - - @SuppressWarnings("unchecked") - @Override - public T[] toArray() { - return (T[]) backing.toArray(); - } - - @Override - public String toString() { - return String.format("QueueStack [backing=%s]", backing); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + (backing == null ? 0 : backing.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof QueueStack)) return false; - - final QueueStack other = (QueueStack) obj; - - if (backing == null) { - if (other.backing != null) return false; - } else if (!backing.equals(other.backing)) return false; - - return true; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/SimpleDirectory.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/SimpleDirectory.java deleted file mode 100644 index 69fd019..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/SimpleDirectory.java +++ /dev/null @@ -1,95 +0,0 @@ -package bjc.utils.esodata; - -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IMap; - -/** - * Simple implementation of {@link Directory}. - * - * Has a split namespace for data and children. - * - * @author EVE - * - * @param - * The key type of the directory. - * @param - * The value type of the directory. - */ -public class SimpleDirectory implements Directory { - private final IMap> children; - - private final IMap data; - - /** - * Create a new directory. - */ - public SimpleDirectory() { - children = new FunctionalMap<>(); - data = new FunctionalMap<>(); - } - - @Override - public Directory getSubdirectory(final K key) { - return children.get(key); - } - - @Override - public boolean hasSubdirectory(final K key) { - return children.containsKey(key); - } - - @Override - public Directory putSubdirectory(final K key, final Directory val) { - return children.put(key, val); - } - - @Override - public boolean containsKey(final K key) { - return data.containsKey(key); - } - - @Override - public V getKey(final K key) { - return data.get(key); - } - - @Override - public V putKey(final K key, final V val) { - return data.put(key, val); - } - - @Override - public int hashCode() { - final int prime = 31; - - int result = 1; - result = prime * result + (children == null ? 0 : children.hashCode()); - result = prime * result + (data == null ? 0 : data.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof SimpleDirectory)) return false; - - final SimpleDirectory other = (SimpleDirectory) obj; - - if (children == null) { - if (other.children != null) return false; - } else if (!children.equals(other.children)) return false; - - if (data == null) { - if (other.data != null) return false; - } else if (!data.equals(other.data)) return false; - - return true; - } - - @Override - public String toString() { - return String.format("SimpleDirectory [children=%s, data=%s]", children, data); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/SimpleStack.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/SimpleStack.java deleted file mode 100644 index fdb3300..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/SimpleStack.java +++ /dev/null @@ -1,88 +0,0 @@ -package bjc.utils.esodata; - -import java.util.Deque; -import java.util.LinkedList; - -/** - * Simple implementation of a stack. - * - * @param - * The datatype stored in the stack. - * @author Ben Culkin - */ -public class SimpleStack extends Stack { - private final Deque backing; - - /** - * Create a new empty stack. - * - */ - public SimpleStack() { - backing = new LinkedList<>(); - } - - @Override - public void push(final T elm) { - backing.push(elm); - } - - @Override - public T pop() { - if (backing.isEmpty()) throw new StackUnderflowException(); - - return backing.pop(); - } - - @Override - public T top() { - if (backing.isEmpty()) throw new StackUnderflowException(); - - return backing.peek(); - } - - @Override - public int size() { - return backing.size(); - } - - @Override - public boolean empty() { - return backing.size() == 0; - } - - @Override - @SuppressWarnings("unchecked") - public T[] toArray() { - return (T[]) backing.toArray(); - } - - @Override - public int hashCode() { - final int prime = 31; - - int result = 1; - result = prime * result + (backing == null ? 0 : backing.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof SimpleStack)) return false; - - final SimpleStack other = (SimpleStack) obj; - - if (backing == null) { - if (other.backing != null) return false; - } else if (!backing.equals(other.backing)) return false; - - return true; - } - - @Override - public String toString() { - return String.format("SimpleStack [backing=%s]", backing); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/SingleTape.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/SingleTape.java deleted file mode 100644 index c50be92..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/SingleTape.java +++ /dev/null @@ -1,255 +0,0 @@ -package bjc.utils.esodata; - -import java.util.ArrayList; - -/** - * A tape is a one-dimensional array that can only be accessed in one position - * at a time. - * - * A tape is essentially a 1D array with a cursor attached to it, and you can - * only affect elements at that cursor. The size of the array is theoretically - * unbounded to the right, but in practice bounded by available memory. - * - * You can choose whether or not you want the tape to automatically extend - * itself to the right with null elements by specifying its auto-extension - * policy. - * - * @param - * The element type of the tape. - * - * @author bjculkin - */ -public class SingleTape implements Tape { - protected ArrayList backing; - protected int pos; - - protected boolean autoExtend; - - /** - * Create a new tape with the specified contents that doesn't - * autoextend. - */ - public SingleTape(T... vals) { - autoExtend = false; - - backing = new ArrayList(vals.length); - - for(T val : vals) { - backing.add(val); - } - } - /** - * Create a new empty tape that doesn't autoextend. - */ - public SingleTape() { - this(false); - } - - /** - * Create a new empty tape that follows the specified auto-extension - * policy. - * - * @param autoExtnd - * Whether or not to auto-extend the tape to the right w/ - * nulls. - */ - public SingleTape(final boolean autoExtnd) { - autoExtend = autoExtnd; - - backing = new ArrayList<>(); - } - - /** - * Get the item the tape is currently on. - * - * @return The item the tape is on. - */ - @Override - public T item() { - return backing.get(pos); - } - - /** - * Set the item the tape is currently on. - * - * @param itm - * The new value for the tape item. - */ - @Override - public void item(final T itm) { - backing.set(pos, itm); - } - - /** - * Get the current number of elements in the tape. - * - * @return The current number of elements in the tape. - */ - @Override - public int size() { - return backing.size(); - } - - @Override - public int position() { - return pos; - } - - /** - * Insert an element before the current item. - * - * @param itm - * The item to add. - */ - @Override - public void insertBefore(final T itm) { - backing.add(pos, itm); - } - - /** - * Insert an element after the current item. - */ - @Override - public void insertAfter(final T itm) { - if (pos == backing.size() - 1) { - backing.add(itm); - } else { - backing.add(pos + 1, itm); - } - } - - /** - * Remove the current element. - * - * Also moves the cursor back one step if possible to maintain relative - * position. - * - * @return The removed item. - */ - @Override - public T remove() { - final T res = backing.remove(pos); - if (pos != 0) { - pos -= 1; - } - return res; - } - - /** - * Move the cursor to the left-most position. - */ - @Override - public void first() { - pos = 0; - } - - /** - * Move the cursor the right-most position. - */ - @Override - public void last() { - pos = backing.size() - 1; - } - - /** - * Move the cursor one space left. - * - * The cursor can't go past zero. - * - * @return True if the cursor was moved left. - */ - @Override - public boolean left() { - return left(1); - } - - /** - * Move the cursor the specified amount left. - * - * The cursor can't go past zero. Attempts to move the cursor by amounts - * that would exceed zero don't move the cursor at all. - * - * @param amt - * The amount to attempt to move the cursor left. - * - * @return True if the cursor was moved left. - */ - @Override - public boolean left(final int amt) { - if (pos - amt < 0) return false; - - pos -= amt; - return true; - } - - /** - * Move the cursor one space right. - * - * Moving the cursor right will auto-extend the tape if that is enabled. - * - * @return Whether the cursor was moved right. - */ - @Override - public boolean right() { - return right(1); - } - - /** - * Move the cursor the specified amount right. - * - * Moving the cursor right will auto-extend the tape if that is enabled. - * - * @param amt - * The amount to move the cursor right by. - * - * @return Whether the cursor was moved right. - */ - @Override - public boolean right(final int amt) { - if (pos + amt >= backing.size() - 1) { - if (autoExtend) { - while (pos + amt >= backing.size() - 1) { - backing.add(null); - } - } else return false; - } - - pos += amt; - return true; - } - - @Override - public boolean isDoubleSided() { - return false; - } - - @Override - public int hashCode() { - final int prime = 31; - - int result = 1; - result = prime * result + (backing == null ? 0 : backing.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof SingleTape)) return false; - - final SingleTape other = (SingleTape) obj; - - if (backing == null) { - if (other.backing != null) return false; - } else if (!backing.equals(other.backing)) return false; - - return true; - } - - @Override - public String toString() { - return String.format("SingleTape [backing=%s, pos=%s, autoExtend=%s]", backing, pos, autoExtend); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/SpaghettiStack.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/SpaghettiStack.java deleted file mode 100644 index 7c8c757..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/SpaghettiStack.java +++ /dev/null @@ -1,99 +0,0 @@ -package bjc.utils.esodata; - -import java.util.Arrays; -import java.util.stream.Stream; - -/* - * Implements a spaghetti stack, which is a stack that is branched off of a - * parent stack. - * - * @param T The datatype stored in the stack. - * @author Ben Culkin - */ -class SpaghettiStack extends Stack { - private final Stack backing; - - private final Stack parent; - - /** - * Create a new empty spaghetti stack, off of the specified parent. - * - * @param par - * The parent stack - */ - public SpaghettiStack(final Stack par) { - backing = new SimpleStack<>(); - - parent = par; - } - - @Override - public void push(final T elm) { - backing.push(elm); - } - - @Override - public T pop() { - if (backing.empty()) return parent.pop(); - - return backing.pop(); - } - - @Override - public T top() { - if (backing.empty()) return parent.top(); - - return backing.top(); - } - - @Override - public int size() { - return parent.size() + backing.size(); - } - - @Override - public boolean empty() { - return backing.empty() && parent.empty(); - } - - @SuppressWarnings("unchecked") - @Override - public T[] toArray() { - return (T[]) Stream.concat(Arrays.stream(parent.toArray()), Arrays.stream(backing.toArray())).toArray(); - } - - @Override - public int hashCode() { - final int prime = 31; - - int result = 1; - result = prime * result + (backing == null ? 0 : backing.hashCode()); - result = prime * result + (parent == null ? 0 : parent.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof SpaghettiStack)) return false; - - final SpaghettiStack other = (SpaghettiStack) obj; - - if (backing == null) { - if (other.backing != null) return false; - } else if (!backing.equals(other.backing)) return false; - - if (parent == null) { - if (other.parent != null) return false; - } else if (!parent.equals(other.parent)) return false; - - return true; - } - - @Override - public String toString() { - return String.format("SpaghettiStack [backing=%s, parent=%s]", backing, parent); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/Stack.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/Stack.java deleted file mode 100644 index 9d74e9a..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/Stack.java +++ /dev/null @@ -1,459 +0,0 @@ -package bjc.utils.esodata; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -/** - * A stack, with support for combinators. - * - * A FILO stack with support for forth/factor style combinators. - * - *

- *

Stack underflow

- *

- * NOTE: In general, using any operation that attempts to remove more data from - * the stack than exists will cause a {@link StackUnderflowException} to be - * thrown. Check the size of the stack if you want to avoid this. - *

- *

- * - * @param - * The datatype stored in the stack. - * - * @author Ben Culkin - */ -public abstract class Stack { - /** - * The exception thrown when attempting to access an element from the - * stack that isn't there. - * - * @author EVE - * - */ - public static class StackUnderflowException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = 1423867176204571539L; - } - - /** - * Push an element onto the stack. - * - * @param elm - * The element to insert. - */ - public abstract void push(T elm); - - /** - * Pop an element off of the stack. - * - * @return The element on top of the stack. - */ - public abstract T pop(); - - /** - * Retrieve the top element of this stack without removing it from the - * stack. - * - * @return The top element of this stack. - */ - public abstract T top(); - - /** - * Get the number of elements in the stack. - * - * @return the number of elements in the stack. - */ - public abstract int size(); - - /** - * Check if the stack is empty. - * - * @return Whether or not the stack is empty. - */ - public abstract boolean empty(); - - /** - * Create a spaghetti stack branching off of this one. - * - * @return A spaghetti stack with this stack as a parent. - */ - public Stack spaghettify() { - return new SpaghettiStack<>(this); - } - - /* - * Basic combinators - */ - - /** - * Drop n items from the stack. - * - * @param n - * The number of items to drop. - */ - public void drop(final int n) { - for (int i = 0; i < n; i++) { - pop(); - } - } - - /** - * Drop one item from the stack. - */ - public void drop() { - drop(1); - } - - /** - * Delete n items below the current one. - * - * @param n - * The number of items below the top to delete. - */ - public void nip(final int n) { - final T elm = pop(); - - drop(n); - - push(elm); - } - - /** - * Delete the second element in the stack. - */ - public void nip() { - nip(1); - } - - /** - * Replicate the top n items of the stack m times. - * - * @param n - * The number of items to duplicate. - * @param m - * The number of times to duplicate items. - */ - public void multidup(final int n, final int m) { - final List lst = new ArrayList<>(n); - - for (int i = n; i > 0; i--) { - lst.set(i - 1, pop()); - } - - for (int i = 0; i < m; i++) { - for (final T elm : lst) { - push(elm); - } - } - } - - /** - * Duplicate the top n items of the stack. - * - * @param n - * The number of items to duplicate. - */ - public void dup(final int n) { - multidup(n, 2); - } - - /** - * Duplicate the top item on the stack. - */ - public void dup() { - dup(1); - } - - /** - * Replicate the n elements below the top one m times. - * - * @param n - * The number of items to duplicate. - * @param m - * The number of times to duplicate items. - */ - public void multiover(final int n, final int m) { - final List lst = new ArrayList<>(n); - - final T elm = pop(); - - for (int i = n; i > 0; i--) { - lst.set(i - 1, pop()); - } - - for (final T nelm : lst) { - push(nelm); - } - push(elm); - - for (int i = 1; i < m; i++) { - for (final T nelm : lst) { - push(nelm); - } - } - } - - /** - * Duplicate the n elements below the top one. - * - * @param n - * The number of items to duplicate. - */ - public void over(final int n) { - multiover(n, 2); - } - - /** - * Duplicate the second item in the stack. - */ - public void over() { - over(1); - } - - /** - * Duplicate the third item in the stack. - */ - public void pick() { - final T z = pop(); - final T y = pop(); - final T x = pop(); - - push(x); - push(y); - push(z); - push(x); - } - - /** - * Swap the top two items on the stack. - */ - public void swap() { - final T y = pop(); - final T x = pop(); - - push(y); - push(x); - } - - /** - * Duplicate the second item below the first item. - */ - public void deepdup() { - final T y = pop(); - final T x = pop(); - - push(x); - push(x); - push(y); - } - - /** - * Swap the second and third items in the stack. - */ - public void deepswap() { - final T z = pop(); - final T y = pop(); - final T x = pop(); - - push(y); - push(x); - push(z); - } - - /** - * Rotate the top three items on the stack - */ - public void rot() { - final T z = pop(); - final T y = pop(); - final T x = pop(); - - push(y); - push(z); - push(x); - } - - /** - * Inversely rotate the top three items on the stack - */ - public void invrot() { - final T z = pop(); - final T y = pop(); - final T x = pop(); - - push(z); - push(x); - push(y); - } - - /* - * Dataflow Combinators - */ - /** - * Hides the top n elements on the stack from cons. - * - * @param n - * The number of elements to hide. - * @param cons - * The action to hide the elements from - */ - public void dip(final int n, final Consumer> cons) { - final List elms = new ArrayList<>(n); - - for (int i = n; i > 0; i--) { - elms.set(i - 1, pop()); - } - - cons.accept(this); - - for (final T elm : elms) { - push(elm); - } - } - - /** - * Hide the top element of the stack from cons. - * - * @param cons - * The action to hide the top from - */ - public void dip(final Consumer> cons) { - dip(1, cons); - } - - /** - * Copy the top n elements on the stack, replacing them once cons is - * done. - * - * @param n - * The number of elements to copy. - * @param cons - * The action to execute. - */ - public void keep(final int n, final Consumer> cons) { - dup(n); - dip(n, cons); - } - - /** - * Apply all the actions in conses to the top n elements of the stack. - * - * @param n - * The number of elements to give to cons. - * @param conses - * The actions to execute. - */ - public void multicleave(final int n, final List>> conses) { - final List elms = new ArrayList<>(n); - - for (int i = n; i > 0; i--) { - elms.set(i - 1, pop()); - } - - for (final Consumer> cons : conses) { - for (final T elm : elms) { - push(elm); - } - - cons.accept(this); - } - } - - /** - * Apply all the actions in conses to the top element of the stack. - * - * @param conses - * The actions to execute. - */ - public void cleave(final List>> conses) { - multicleave(1, conses); - } - - /** - * Apply every action in cons to n arguments. - * - * @param n - * The number of parameters each action takes. - * @param conses - * The actions to execute. - */ - public void multispread(final int n, final List>> conses) { - final List> nelms = new ArrayList<>(conses.size()); - - for (int i = conses.size(); i > 0; i--) { - final List elms = new ArrayList<>(n); - - for (int j = n; j > 0; j--) { - elms.set(j, pop()); - } - - nelms.set(i, elms); - } - - int i = 0; - for (final List elms : nelms) { - for (final T elm : elms) { - push(elm); - } - - conses.get(i).accept(this); - i += 1; - } - } - - /** - * Apply the actions in cons to corresponding elements from the stack. - * - * @param conses - * The actions to execute. - */ - public void spread(final List>> conses) { - multispread(1, conses); - } - - /** - * Apply the action in cons to the first m groups of n arguments. - * - * @param n - * The number of arguments cons takes. - * @param m - * The number of time to call cons. - * @param cons - * The action to execute. - */ - public void multiapply(final int n, final int m, final Consumer> cons) { - final List>> conses = new ArrayList<>(m); - - for (int i = 0; i < m; i++) { - conses.add(cons); - } - - multispread(n, conses); - } - - /** - * Apply cons n times to the corresponding elements in the stack. - * - * @param n - * The number of times to execute cons. - * @param cons - * The action to execute. - */ - public void apply(final int n, final Consumer> cons) { - multiapply(1, n, cons); - } - - /* - * Misc. functions - */ - /** - * Get an array representing this stack. - * - * @return The stack as an array. - */ - public abstract T[] toArray(); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/Tape.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/Tape.java deleted file mode 100644 index b6a2c01..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/Tape.java +++ /dev/null @@ -1,126 +0,0 @@ -package bjc.utils.esodata; - -/** - * Interface for something that acts like a tape. - * - * A tape is essentially a 1D array with a cursor attached to it, and you can - * only affect elements at that cursor. The size of the array is theoretically - * unbounded to the right, but in practice bounded by available memory. - * - * @param - * The element type of the tape. - * - * @author bjculkin - */ -public interface Tape { - /** - * Get the item the tape is currently on. - * - * @return The item the tape is on. - */ - T item(); - - /** - * Set the item the tape is currently on. - * - * @param itm - * The new value for the tape item. - */ - void item(T itm); - - /** - * Get the current number of elements in the tape. - * - * @return The current number of elements in the tape. - */ - int size(); - - /** - * Get the position of the current item. - * - * @return The position of the current item. - */ - int position(); - - /** - * Insert an element before the current item. - * - * @param itm - * The item to add. - */ - void insertBefore(T itm); - - /** - * Insert an element after the current item. - * - * @param itm - * The item to insert. - */ - void insertAfter(T itm); - - /** - * Remove the current element. - * - * Also moves the cursor back one step if possible to maintain relative - * position. - * - * @return The removed item. - */ - T remove(); - - /** - * Move the cursor to the left-most position. - */ - void first(); - - /** - * Move the cursor the right-most position. - */ - void last(); - - /** - * Move the cursor one space left. - * - * The cursor can't go past zero. - * - * @return True if the cursor was moved left. - */ - boolean left(); - - /** - * Move the cursor the specified amount left. - * - * The cursor can't go past zero. Attempts to move the cursor by amounts - * that would exceed zero don't move the cursor at all. - * - * @param amt - * The amount to attempt to move the cursor left. - * - * @return True if the cursor was moved left. - */ - boolean left(int amt); - - /** - * Move the cursor one space right. - * - * @return Whether the cursor was moved right. - */ - boolean right(); - - /** - * Move the cursor the specified amount right. - * - * @param amt - * The amount to move the cursor right by. - * - * @return Whether the cursor was moved right. - */ - boolean right(int amt); - - /** - * Is this tape double sided? - * - * @return Whether or not this tape is double-sided. - */ - boolean isDoubleSided(); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/TapeChanger.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/TapeChanger.java deleted file mode 100644 index dc885bc..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/TapeChanger.java +++ /dev/null @@ -1,363 +0,0 @@ -package bjc.utils.esodata; - -/** - * A tape changer is essentially a tape of tapes. - * - * It has a current tape that you can do operations to, but also operations to - * add/remove other tapes. - * - * If there is no tape currently loaded into the changer, all the methods will - * either return null/false. - * - * @param - * The element type of the tapes. - */ -public class TapeChanger implements Tape { - private Tape> tapes; - private Tape currentTape; - - /** - * Create a new empty tape changer. - */ - public TapeChanger() { - tapes = new SingleTape<>(); - } - - /** - * Create a new tape changer with the specified tapes. - * - * @param current - * The tape to mount first. - * @param others - * The tapes to put in this tape changer. - */ - @SafeVarargs - public TapeChanger(final Tape current, final Tape... others) { - this(); - - tapes.insertBefore(current); - - for (final Tape tp : others) { - tapes.insertAfter(tp); - tapes.right(); - } - - tapes.first(); - currentTape = tapes.item(); - } - - /** - * Get the item the tape is currently on. - * - * @return The item the tape is on. - */ - @Override - public T item() { - if (currentTape == null) return null; - - return currentTape.item(); - } - - /** - * Set the item the tape is currently on. - * - * @param itm - * The new value for the tape item. - */ - @Override - public void item(final T itm) { - if (currentTape == null) return; - - currentTape.item(itm); - } - - /** - * Get the current number of elements in the tape. - * - * @return The current number of elements in the tape. - */ - @Override - public int size() { - if (currentTape == null) return 0; - - return currentTape.size(); - } - - @Override - public int position() { - if (currentTape == null) return 0; - - return currentTape.position(); - } - - /** - * Insert an element before the current item. - * - * @param itm - * The item to add. - */ - @Override - public void insertBefore(final T itm) { - if (currentTape == null) return; - - currentTape.insertBefore(itm); - } - - /** - * Insert an element after the current item. - */ - @Override - public void insertAfter(final T itm) { - if (currentTape == null) return; - - currentTape.insertAfter(itm); - } - - /** - * Remove the current element. - * - * Also moves the cursor back one step if possible to maintain relative - * position, and removes the corresponding item from the non-active side - * - * @return The removed item from the active side. - */ - @Override - public T remove() { - if (currentTape == null) return null; - - return currentTape.remove(); - } - - /** - * Move the cursor to the left-most position. - */ - @Override - public void first() { - if (currentTape == null) return; - - currentTape.first(); - } - - /** - * Move the cursor the right-most position. - */ - @Override - public void last() { - if (currentTape == null) return; - - currentTape.last(); - } - - /** - * Move the cursor one space left. - * - * The cursor can't go past zero. - * - * @return True if the cursor was moved left. - */ - @Override - public boolean left() { - return left(1); - } - - /** - * Move the cursor the specified amount left. - * - * The cursor can't go past zero. Attempts to move the cursor by amounts - * that would exceed zero don't move the cursor at all. - * - * @param amt - * The amount to attempt to move the cursor left. - * - * @return True if the cursor was moved left. - */ - @Override - public boolean left(final int amt) { - if (currentTape == null) return false; - - return currentTape.left(amt); - } - - /** - * Move the cursor one space right. - * - * Moving the cursor right will auto-extend the tape if that is enabled. - * - * @return Whether the cursor was moved right. - */ - @Override - public boolean right() { - return right(1); - } - - /** - * Move the cursor the specified amount right. - * - * Moving the cursor right will auto-extend the tape if that is enabled. - * - * @param amt - * The amount to move the cursor right by. - * - * @return Whether the cursor was moved right. - */ - @Override - public boolean right(final int amt) { - if (currentTape == null) return false; - - return currentTape.right(amt); - } - - /** - * Flips the tape. - * - * The active side becomes inactive, and the inactive side becomes - * active. - * - * If the current tape is not double-sided, does nothing. - */ - public void flip() { - if (currentTape == null) return; - - if (currentTape.isDoubleSided()) { - ((DoubleTape) currentTape).flip(); - } - } - - @Override - public boolean isDoubleSided() { - if (currentTape == null) return false; - - return currentTape.isDoubleSided(); - } - - /** - * Check if a tape is currently loaded. - * - * @return Whether or not a tape is loaded. - */ - public boolean isLoaded() { - return currentTape != null; - } - - /** - * Move to the next tape in the changer. - * - * Attempting to load a tape that isn't there won't eject the current - * tape. - * - * @return Whether or not the next tape was loaded. - */ - public boolean nextTape() { - final boolean succ = tapes.right(); - - if (succ) { - currentTape = tapes.item(); - } - - return succ; - } - - /** - * Move to the previous tape in the changer. - * - * Attempting to load a tape that isn't there won't eject the current - * tape. - * - * @return Whether or not the previous tape was loaded. - */ - public boolean prevTape() { - final boolean succ = tapes.left(); - - if (succ) { - currentTape = tapes.item(); - } - - return succ; - } - - /** - * Inserts a tape into the tape changer. - * - * Any currently loaded tape is ejected, and becomes the previous tape. - * - * The specified tape is loaded. - * - * @param tp - * The tape to insert and load. - */ - public void insertTape(final Tape tp) { - tapes.insertAfter(tp); - tapes.right(); - - currentTape = tapes.item(); - } - - /** - * Removes the current tape. - * - * Does nothing if there is not a tape loaded. - * - * Loads the previous tape, if there is one. - * - * @return The removed tape. - */ - public Tape removeTape() { - if (currentTape == null) return null; - - final Tape tp = tapes.remove(); - currentTape = tapes.item(); - - return tp; - } - - /** - * Ejects the current tape. - * - * Does nothing if no tape is loaded. - */ - public void eject() { - currentTape = null; - } - - /** - * Get how many tapes are currently in the changer. - * - * @return How many tapes are currently in the changer. - */ - public int tapeCount() { - return tapes.size(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (currentTape == null ? 0 : currentTape.hashCode()); - result = prime * result + (tapes == null ? 0 : tapes.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof TapeChanger)) return false; - - final TapeChanger other = (TapeChanger) obj; - - if (currentTape == null) { - if (other.currentTape != null) return false; - } else if (!currentTape.equals(other.currentTape)) return false; - - if (tapes == null) { - if (other.tapes != null) return false; - } else if (!tapes.equals(other.tapes)) return false; - - return true; - } - - @Override - public String toString() { - return String.format("TapeChanger [tapes=%s, currentTape='%s']", tapes, currentTape); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/TapeLibrary.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/TapeLibrary.java deleted file mode 100644 index 2dbc70b..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/TapeLibrary.java +++ /dev/null @@ -1,340 +0,0 @@ -package bjc.utils.esodata; - -import java.util.HashMap; -import java.util.Map; - -/** - * A tape changer is essentially a map of tapes. - * - * It has a current tape that you can do operations to, but also operations to - * add/remove other tapes. - * - * If there is no tape currently loaded into the changer, all the methods will - * either return null/false. - * - * @param - * The element type of the tapes. - */ -public class TapeLibrary implements Tape { - private final Map> tapes; - private Tape currentTape; - - /** - * Create a new empty tape library. - */ - public TapeLibrary() { - tapes = new HashMap<>(); - } - - /** - * Get the item the tape is currently on. - * - * @return The item the tape is on. - */ - @Override - public T item() { - if (currentTape == null) return null; - - return currentTape.item(); - } - - /** - * Set the item the tape is currently on. - * - * @param itm - * The new value for the tape item. - */ - @Override - public void item(final T itm) { - if (currentTape == null) return; - - currentTape.item(itm); - } - - /** - * Get the current number of elements in the tape. - * - * @return The current number of elements in the tape. - */ - @Override - public int size() { - if (currentTape == null) return 0; - - return currentTape.size(); - } - - @Override - public int position() { - if (currentTape == null) return 0; - - return currentTape.position(); - } - /** - * Insert an element before the current item. - * - * @param itm - * The item to add. - */ - @Override - public void insertBefore(final T itm) { - if (currentTape == null) return; - - currentTape.insertBefore(itm); - } - - /** - * Insert an element after the current item. - */ - @Override - public void insertAfter(final T itm) { - if (currentTape == null) return; - - currentTape.insertAfter(itm); - } - - /** - * Remove the current element. - * - * Also moves the cursor back one step if possible to maintain relative - * position, and removes the corresponding item from the non-active side - * - * @return The removed item from the active side. - */ - @Override - public T remove() { - if (currentTape == null) return null; - - return currentTape.remove(); - } - - /** - * Move the cursor to the left-most position. - */ - @Override - public void first() { - if (currentTape == null) return; - - currentTape.first(); - } - - /** - * Move the cursor the right-most position. - */ - @Override - public void last() { - if (currentTape == null) return; - - currentTape.last(); - } - - /** - * Move the cursor one space left. - * - * The cursor can't go past zero. - * - * @return True if the cursor was moved left. - */ - @Override - public boolean left() { - return left(1); - } - - /** - * Move the cursor the specified amount left. - * - * The cursor can't go past zero. Attempts to move the cursor by amounts - * that would exceed zero don't move the cursor at all. - * - * @param amt - * The amount to attempt to move the cursor left. - * - * @return True if the cursor was moved left. - */ - @Override - public boolean left(final int amt) { - if (currentTape == null) return false; - - return currentTape.left(amt); - } - - /** - * Move the cursor one space right. - * - * Moving the cursor right will auto-extend the tape if that is enabled. - * - * @return Whether the cursor was moved right. - */ - @Override - public boolean right() { - return right(1); - } - - /** - * Move the cursor the specified amount right. - * - * Moving the cursor right will auto-extend the tape if that is enabled. - * - * @param amt - * The amount to move the cursor right by. - * - * @return Whether the cursor was moved right. - */ - @Override - public boolean right(final int amt) { - if (currentTape == null) return false; - - return currentTape.right(amt); - } - - /** - * Flips the tape. - * - * The active side becomes inactive, and the inactive side becomes - * active. - * - * If the current tape is not double-sided, does nothing. - */ - public void flip() { - if (currentTape == null) return; - - if (currentTape.isDoubleSided()) { - ((DoubleTape) currentTape).flip(); - } - } - - @Override - public boolean isDoubleSided() { - if (currentTape == null) return false; - - return currentTape.isDoubleSided(); - } - - /** - * Check if a tape is currently loaded. - * - * @return Whether or not a tape is loaded. - */ - public boolean isLoaded() { - return currentTape != null; - } - - /** - * Move to the specified tape in the library. - * - * Attempting to load a tape that isn't there won't eject the current - * tape. - * - * @param label - * The label of the tape to load. - * - * @return Whether or not the next tape was loaded. - */ - public boolean switchTape(final String label) { - if (tapes.containsKey(label)) { - currentTape = tapes.get(label); - return true; - } - - return false; - } - - /** - * Inserts a tape into the tape library. - * - * Any currently loaded tape is ejected. - * - * The specified tape is loaded. - * - * Adding a duplicate tape will overwrite any existing types. - * - * @param label - * The label of the tape to add. - * - * @param tp - * The tape to insert and load. - */ - public void insertTape(final String label, final Tape tp) { - tapes.put(label, tp); - - currentTape = tp; - } - - /** - * Remove a tape from the library. - * - * Does nothing if there is not a tape of that name loaded. - * - * @param label - * The tape to remove. - * - * @return The removed tape. - */ - public Tape removeTape(final String label) { - return tapes.remove(label); - } - - /** - * Ejects the current tape. - * - * Does nothing if no tape is loaded. - */ - public void eject() { - currentTape = null; - } - - /** - * Get how many tapes are currently in the library. - * - * @return How many tapes are currently in the library. - */ - public int tapeCount() { - return tapes.size(); - } - - /** - * Check if a specific tape is loaded into the library. - * - * @param label - * The tape to check for. - * - * @return Whether or not a tape of that name exists - */ - public boolean hasTape(final String label) { - return tapes.containsKey(label); - } - - @Override - public int hashCode() { - final int prime = 31; - - int result = 1; - result = prime * result + (currentTape == null ? 0 : currentTape.hashCode()); - result = prime * result + (tapes == null ? 0 : tapes.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof TapeLibrary)) return false; - - final TapeLibrary other = (TapeLibrary) obj; - - if (currentTape == null) { - if (other.currentTape != null) return false; - } else if (!currentTape.equals(other.currentTape)) return false; - - if (tapes == null) { - if (other.tapes != null) return false; - } else if (!tapes.equals(other.tapes)) return false; - - return true; - } - - @Override - public String toString() { - return String.format("TapeLibrary [tapes=%s, currentTape='%s']", tapes, currentTape); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/esodata/UnifiedDirectory.java b/BJC-Utils2/src/main/java/bjc/utils/esodata/UnifiedDirectory.java deleted file mode 100644 index ffb639f..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/esodata/UnifiedDirectory.java +++ /dev/null @@ -1,105 +0,0 @@ -package bjc.utils.esodata; - -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IMap; - -/** - * Simple implementation of {@link Directory}. - * - * Has a unified namespace for data and children. - * - * @author EVE - * - * @param - * The key type of the directory. - * @param - * The value type of the directory. - */ -public class UnifiedDirectory implements Directory { - private final IMap> children; - - private final IMap data; - - /** - * Create a new directory. - */ - public UnifiedDirectory() { - children = new FunctionalMap<>(); - data = new FunctionalMap<>(); - } - - @Override - public Directory getSubdirectory(final K key) { - return children.get(key); - } - - @Override - public boolean hasSubdirectory(final K key) { - return children.containsKey(key); - } - - @Override - public Directory putSubdirectory(final K key, final Directory val) { - if (data.containsKey(key)) { - final String msg = String.format("Key %s is already used for data", key); - - throw new IllegalArgumentException(msg); - } - - return children.put(key, val); - } - - @Override - public boolean containsKey(final K key) { - return data.containsKey(key); - } - - @Override - public V getKey(final K key) { - return data.get(key); - } - - @Override - public V putKey(final K key, final V val) { - if (children.containsKey(key)) { - final String msg = String.format("Key %s is already used for sub-directories.", key); - - throw new IllegalArgumentException(msg); - } - - return data.put(key, val); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (children == null ? 0 : children.hashCode()); - result = prime * result + (data == null ? 0 : data.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof UnifiedDirectory)) return false; - - final UnifiedDirectory other = (UnifiedDirectory) obj; - - if (children == null) { - if (other.children != null) return false; - } else if (!children.equals(other.children)) return false; - - if (data == null) { - if (other.data != null) return false; - } else if (!data.equals(other.data)) return false; - - return true; - } - - @Override - public String toString() { - return String.format("UnifiedDirectory [children=%s, data=%s]", children, data); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/exceptions/FileNotChosenException.java b/BJC-Utils2/src/main/java/bjc/utils/exceptions/FileNotChosenException.java deleted file mode 100644 index 6f5a68a..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/exceptions/FileNotChosenException.java +++ /dev/null @@ -1,31 +0,0 @@ -package bjc.utils.exceptions; - -import java.io.IOException; - -/** - * Represents the user failing to choose a file. - * - * @author ben - * - */ -public class FileNotChosenException extends IOException { - // Version ID for serialization - private static final long serialVersionUID = -8753348705210831096L; - - /** - * Create a new exception - */ - public FileNotChosenException() { - super(); - } - - /** - * Create a new exception with the given cause - * - * @param cause - * The cause of why the exception was thrown - */ - public FileNotChosenException(final String cause) { - super(cause); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/exceptions/PragmaFormatException.java b/BJC-Utils2/src/main/java/bjc/utils/exceptions/PragmaFormatException.java deleted file mode 100644 index 1ad339d..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/exceptions/PragmaFormatException.java +++ /dev/null @@ -1,31 +0,0 @@ -package bjc.utils.exceptions; - -import java.util.InputMismatchException; - -/** - * The exception to throw whenever a pragma is used with invalid syntax - * - * @author ben - * - */ -public class PragmaFormatException extends InputMismatchException { - // Version ID for serialization - private static final long serialVersionUID = 1288536477368021069L; - - /** - * Create a new exception - */ - public PragmaFormatException() { - super(); - } - - /** - * Create a new exception with the given message - * - * @param message - * The message to explain why the exception was thrown - */ - public PragmaFormatException(final String message) { - super(message); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/exceptions/UnknownPragmaException.java b/BJC-Utils2/src/main/java/bjc/utils/exceptions/UnknownPragmaException.java deleted file mode 100644 index 6fc9113..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/exceptions/UnknownPragmaException.java +++ /dev/null @@ -1,25 +0,0 @@ -package bjc.utils.exceptions; - -import java.util.InputMismatchException; - -/** - * Represents a error from encountering a unknown pragma - * - * @author ben - * - */ -public class UnknownPragmaException extends InputMismatchException { - // Version ID for serialization - private static final long serialVersionUID = -4277573484926638662L; - - /** - * Create a new exception with the given cause - * - * @param cause - * The cause for throwing this exception - */ - public UnknownPragmaException(final String cause) { - super(cause); - } - -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/ExtendedMap.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/ExtendedMap.java deleted file mode 100644 index 909c5e9..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/ExtendedMap.java +++ /dev/null @@ -1,127 +0,0 @@ -package bjc.utils.funcdata; - -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; - -import bjc.utils.funcutils.ListUtils; - -class ExtendedMap implements IMap { - private final IMap delegate; - - private final IMap store; - - public ExtendedMap(final IMap delegate, final IMap store) { - this.delegate = delegate; - this.store = store; - } - - @Override - public void clear() { - store.clear(); - } - - @Override - public boolean containsKey(final KeyType key) { - if (store.containsKey(key)) return true; - - return delegate.containsKey(key); - } - - @Override - public IMap extend() { - return new ExtendedMap<>(this, new FunctionalMap<>()); - } - - @Override - public void forEach(final BiConsumer action) { - store.forEach(action); - - delegate.forEach(action); - } - - @Override - public void forEachKey(final Consumer action) { - store.forEachKey(action); - - delegate.forEachKey(action); - } - - @Override - public void forEachValue(final Consumer action) { - store.forEachValue(action); - - delegate.forEachValue(action); - } - - @Override - public ValueType get(final KeyType key) { - if (store.containsKey(key)) return store.get(key); - - return delegate.get(key); - } - - @Override - public int size() { - return store.size() + delegate.size(); - } - - @Override - public IList keyList() { - return ListUtils.mergeLists(store.keyList(), delegate.keyList()); - } - - @Override - public IMap transform(final Function transformer) { - return new TransformedValueMap<>(this, transformer); - } - - @Override - public ValueType put(final KeyType key, final ValueType val) { - return store.put(key, val); - } - - @Override - public ValueType remove(final KeyType key) { - if (!store.containsKey(key)) return delegate.remove(key); - - return store.remove(key); - } - - @Override - public IList valueList() { - return ListUtils.mergeLists(store.valueList(), delegate.valueList()); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (delegate == null ? 0 : delegate.hashCode()); - result = prime * result + (store == null ? 0 : store.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof ExtendedMap)) return false; - - final ExtendedMap other = (ExtendedMap) obj; - - if (delegate == null) { - if (other.delegate != null) return false; - } else if (!delegate.equals(other.delegate)) return false; - if (store == null) { - if (other.store != null) return false; - } else if (!store.equals(other.store)) return false; - - return true; - } - - @Override - public String toString() { - return String.format("ExtendedMap [delegate=%s, store=%s]", delegate, store); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalList.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalList.java deleted file mode 100644 index 55ea7ff..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalList.java +++ /dev/null @@ -1,423 +0,0 @@ -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.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; - -import bjc.utils.data.IHolder; -import bjc.utils.data.IPair; -import bjc.utils.data.Identity; -import bjc.utils.data.Pair; - -/** - * A wrapper over another list that provides eager functional operations over - * it. - * - * Differs from a stream in every way except for the fact that they both provide - * functional operations. - * - * @author ben - * - * @param - * The type in this list - */ -public class FunctionalList implements Cloneable, IList { - /* - * The list used as a backing store - */ - private final List wrapped; - - /** - * Create a new empty functional list. - */ - public FunctionalList() { - wrapped = new ArrayList<>(); - } - - /** - * Create a new functional list containing the specified items. - * - * Takes O(n) time, where n is the number of items specified - * - * @param items - * The items to put into this functional list. - */ - @SafeVarargs - public FunctionalList(final E... items) { - wrapped = new ArrayList<>(items.length); - - for (final E item : items) { - wrapped.add(item); - } - } - - /** - * Create a new functional list with the specified size. - * - * @param size - * The size of the backing list . - */ - private FunctionalList(final int size) { - wrapped = new ArrayList<>(size); - } - - /** - * Create a new functional list as a wrapper of a existing list. - * - * Takes O(1) time, since it doesn't copy the list. - * - * @param backing - * The list to use as a backing list. - */ - public FunctionalList(final List backing) { - if (backing == null) throw new NullPointerException("Backing list must be non-null"); - - wrapped = backing; - } - - @Override - public boolean add(final E item) { - return wrapped.add(item); - } - - @Override - public boolean allMatch(final Predicate predicate) { - if (predicate == null) throw new NullPointerException("Predicate must be non-null"); - - for (final E item : wrapped) { - if (!predicate.test(item)) - // We've found a non-matching item - return false; - } - - // All of the items matched - return true; - } - - @Override - public boolean anyMatch(final Predicate predicate) { - if (predicate == null) throw new NullPointerException("Predicate must be not null"); - - for (final E item : wrapped) { - if (predicate.test(item)) - // We've found a matching item - return true; - } - - // We didn't find a matching item - return false; - } - - /** - * Clone this list into a new one, and clone the backing list as well - * - * Takes O(n) time, where n is the number of elements in the list - * - * @return A list - */ - @Override - public IList clone() { - final IList cloned = new FunctionalList<>(); - - for (final E element : wrapped) { - cloned.add(element); - } - - return cloned; - } - - @Override - public IList combineWith(final IList rightList, final 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"); - - final IList returned = new FunctionalList<>(); - - // Get the iterator for the other list - final Iterator rightIterator = rightList.toIterable().iterator(); - - for (final Iterator leftIterator = wrapped.iterator(); leftIterator.hasNext() - && rightIterator.hasNext();) { - // Add the transformed items to the result list - final E leftVal = leftIterator.next(); - final T rightVal = rightIterator.next(); - - returned.add(itemCombiner.apply(leftVal, rightVal)); - } - - return returned; - } - - @Override - public boolean contains(final E item) { - // Check if any items in the list match the provided item - return this.anyMatch(item::equals); - } - - @Override - public E first() { - if (wrapped.size() < 1) - throw new NoSuchElementException("Attempted to get first element of empty list"); - - return wrapped.get(0); - } - - @Override - public IList flatMap(final Function> expander) { - if (expander == null) throw new NullPointerException("Expander must not be null"); - - final IList returned = new FunctionalList<>(this.wrapped.size()); - - forEach(element -> { - final IList expandedElement = expander.apply(element); - - if (expandedElement == null) throw new NullPointerException("Expander returned null list"); - - // Add each element to the returned list - expandedElement.forEach(returned::add); - }); - - return returned; - } - - @Override - public void forEach(final Consumer action) { - if (action == null) throw new NullPointerException("Action is null"); - - wrapped.forEach(action); - } - - @Override - public void forEachIndexed(final 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 - final IHolder currentIndex = new Identity<>(0); - - wrapped.forEach((element) -> { - // Call the action with the index and the value - indexedAction.accept(currentIndex.unwrap(index -> index), element); - - // Increment the value - currentIndex.transform((index) -> index + 1); - }); - } - - @Override - public E getByIndex(final int index) { - return wrapped.get(index); - } - - /** - * Get the internal backing list. - * - * @return The backing list this list is based off of. - */ - public List getInternal() { - return wrapped; - } - - @Override - public IList getMatching(final Predicate predicate) { - if (predicate == null) throw new NullPointerException("Predicate must not be null"); - - final IList returned = new FunctionalList<>(); - - wrapped.forEach((element) -> { - if (predicate.test(element)) { - // The item matches, so add it to the returned - // list - returned.add(element); - } - }); - - return returned; - } - - @Override - public int getSize() { - return wrapped.size(); - } - - @Override - public boolean isEmpty() { - return wrapped.isEmpty(); - } - - /* - * Check if a partition has room for another item - */ - private Boolean isPartitionFull(final int numberPerPartition, final IHolder> currentPartition) { - return currentPartition.unwrap((partition) -> partition.getSize() >= numberPerPartition); - } - - @Override - public IList map(final Function elementTransformer) { - if (elementTransformer == null) throw new NullPointerException("Transformer must be not null"); - - final IList returned = new FunctionalList<>(this.wrapped.size()); - - forEach(element -> { - // Add the transformed item to the result - returned.add(elementTransformer.apply(element)); - }); - - return returned; - } - - @Override - public IList> pairWith(final IList rightList) { - return combineWith(rightList, Pair::new); - } - - @Override - public IList> partition(final int numberPerPartition) { - if (numberPerPartition < 1 || numberPerPartition > wrapped.size()) { - final String fmt = "%s is an invalid partition size. Must be between 1 and %d"; - final String msg = String.format(fmt, numberPerPartition, wrapped.size()); - - throw new IllegalArgumentException(msg); - } - - final IList> returned = new FunctionalList<>(); - - // The current partition being filled - final IHolder> currentPartition = new Identity<>(new FunctionalList<>()); - - this.forEach(element -> { - if (isPartitionFull(numberPerPartition, currentPartition)) { - // Add the partition to the list - returned.add(currentPartition.unwrap(partition -> partition)); - - // Start a new partition - currentPartition.transform(partition -> new FunctionalList<>()); - } else { - // Add the element to the current partition - currentPartition.unwrap(partition -> partition.add(element)); - } - }); - - return returned; - } - - @Override - public void prepend(final E item) { - wrapped.add(0, item); - } - - @Override - public E randItem(final Function rnd) { - if (rnd == null) throw new NullPointerException("Random source must not be null"); - - final int randomIndex = rnd.apply(wrapped.size()); - - return wrapped.get(randomIndex); - } - - @Override - public F reduceAux(final T initialValue, final BiFunction stateAccumulator, - final 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 - final IHolder currentState = new Identity<>(initialValue); - - wrapped.forEach(element -> { - // Accumulate a new value into the state - currentState.transform(state -> stateAccumulator.apply(element, state)); - }); - - // Convert the state to its final value - return currentState.unwrap(resultTransformer); - } - - @Override - public boolean removeIf(final Predicate removePredicate) { - if (removePredicate == null) throw new NullPointerException("Predicate must be non-null"); - - return wrapped.removeIf(removePredicate); - } - - @Override - public void removeMatching(final E desiredElement) { - removeIf(element -> element.equals(desiredElement)); - } - - @Override - public void reverse() { - Collections.reverse(wrapped); - } - - @Override - public E search(final E searchKey, final Comparator comparator) { - // Search our internal list - final int foundIndex = Collections.binarySearch(wrapped, searchKey, comparator); - - if (foundIndex >= 0) // We found a matching element - return wrapped.get(foundIndex); - - // We didn't find an element - return null; - } - - @Override - public void sort(final Comparator comparator) { - // sb.deleteCharAt(sb.length() - 2); - Collections.sort(wrapped, comparator); - } - - @Override - public IList tail() { - return new FunctionalList<>(wrapped.subList(1, getSize())); - } - - @Override - public E[] toArray(final E[] arrType) { - return wrapped.toArray(arrType); - } - - @Override - public Iterable toIterable() { - return wrapped; - } - - @Override - public String toString() { - final int lSize = getSize(); - - if (lSize == 0) return "()"; - - final StringBuilder sb = new StringBuilder("("); - final Iterator itr = toIterable().iterator(); - final E itm = itr.next(); - int i = 0; - - if (lSize == 1) return "(" + itm + ")"; - - for (final E item : toIterable()) { - sb.append(item.toString()); - - if (i < lSize - 1) { - sb.append(", "); - } - - i += 1; - } - - sb.append(")"); - - return sb.toString(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java deleted file mode 100644 index c4f0ff1..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java +++ /dev/null @@ -1,175 +0,0 @@ -package bjc.utils.funcdata; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; - -import bjc.utils.data.IPair; - -/** - * Basic implementation of {@link IMap} - * - * @author ben - * - * @param - * The type of the map's keys - * @param - * The type of the map's values - */ -public class FunctionalMap implements IMap { - private Map wrappedMap; - - /** - * Create a new blank functional map - */ - public FunctionalMap() { - wrappedMap = new HashMap<>(); - } - - /** - * Create a new functional map with the specified entries - * - * @param entries - * The entries to put into the map - */ - @SafeVarargs - public FunctionalMap(final IPair... entries) { - this(); - - for (final IPair entry : entries) { - entry.doWith((key, val) -> { - wrappedMap.put(key, val); - }); - } - } - - /** - * Create a new functional map wrapping the specified map - * - * @param wrap - * The map to wrap - */ - public FunctionalMap(final Map wrap) { - if (wrap == null) throw new NullPointerException("Map to wrap must not be null"); - - wrappedMap = wrap; - } - - @Override - public void clear() { - wrappedMap.clear(); - } - - @Override - public boolean containsKey(final KeyType key) { - return wrappedMap.containsKey(key); - } - - @Override - public IMap extend() { - return new ExtendedMap<>(this, new FunctionalMap<>()); - } - - @Override - public void forEach(final BiConsumer action) { - wrappedMap.forEach(action); - } - - @Override - public void forEachKey(final Consumer action) { - wrappedMap.keySet().forEach(action); - } - - @Override - public void forEachValue(final Consumer action) { - wrappedMap.values().forEach(action); - } - - @Override - public ValueType get(final KeyType key) { - if (key == null) throw new NullPointerException("Key must not be null"); - - if (!wrappedMap.containsKey(key)) { - final String msg = String.format("Key %s is not present in the map", key); - - throw new IllegalArgumentException(msg); - } - - return wrappedMap.get(key); - } - - @Override - public int size() { - return wrappedMap.size(); - } - - @Override - public IList keyList() { - final FunctionalList keys = new FunctionalList<>(); - - wrappedMap.keySet().forEach(key -> { - keys.add(key); - }); - - return keys; - } - - @Override - public IMap transform(final Function transformer) { - if (transformer == null) throw new NullPointerException("Transformer must not be null"); - - return new TransformedValueMap<>(this, transformer); - } - - @Override - public ValueType put(final KeyType key, final ValueType val) { - if (key == null) throw new NullPointerException("Key must not be null"); - - return wrappedMap.put(key, val); - } - - @Override - public ValueType remove(final KeyType key) { - return wrappedMap.remove(key); - } - - @Override - public String toString() { - return wrappedMap.toString(); - } - - @Override - public IList valueList() { - final FunctionalList values = new FunctionalList<>(); - - wrappedMap.values().forEach(value -> { - values.add(value); - }); - - return values; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (wrappedMap == null ? 0 : wrappedMap.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof FunctionalMap)) return false; - - final FunctionalMap other = (FunctionalMap) obj; - - if (wrappedMap == null) { - if (other.wrappedMap != null) return false; - } else if (!wrappedMap.equals(other.wrappedMap)) return false; - return true; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java deleted file mode 100644 index e068b46..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java +++ /dev/null @@ -1,159 +0,0 @@ -package bjc.utils.funcdata; - -import java.util.StringTokenizer; -import java.util.function.Consumer; -import java.util.function.Function; - -/** - * A string tokenizer that exposes a functional interface - * - * @author ben - * - */ -public class FunctionalStringTokenizer { - /** - * Create a new tokenizer from the specified string. - * - * @param strang - * The string to create a tokenizer from. - * @return A new tokenizer that splits the provided string on spaces. - */ - public static FunctionalStringTokenizer fromString(final String strang) { - if (strang == null) throw new NullPointerException("String to tokenize must be non-null"); - - return new FunctionalStringTokenizer(new StringTokenizer(strang, " ")); - } - - /* - * The string tokenizer being driven - */ - private final StringTokenizer input; - - /** - * Create a functional string tokenizer from a given string - * - * @param inp - * The string to tokenize - */ - public FunctionalStringTokenizer(final String inp) { - if (inp == null) throw new NullPointerException("String to tokenize must be non-null"); - - this.input = new StringTokenizer(inp); - } - - /** - * Create a functional string tokenizer from a given string and set of - * separators - * - * @param input - * The string to tokenize - * @param seperators - * The set of separating tokens to use for splitting - */ - public FunctionalStringTokenizer(final String input, final String seperators) { - if (input == 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(input, seperators); - } - - /** - * Create a functional string tokenizer from a non-functional one - * - * @param toWrap - * The non-functional string tokenizer to wrap - */ - public FunctionalStringTokenizer(final StringTokenizer toWrap) { - if (toWrap == null) throw new NullPointerException("Wrapped tokenizer must not be null"); - - this.input = toWrap; - } - - /** - * Execute a provided action for each of the remaining tokens - * - * @param action - * The action to execute for each token - */ - public void forEachToken(final Consumer action) { - if (action == null) throw new NullPointerException("Action must not be null"); - - while (input.hasMoreTokens()) { - action.accept(input.nextToken()); - } - } - - /** - * Get the string tokenizer encapsulated by this tokenizer - * - * @return The encapsulated tokenizer - */ - public StringTokenizer getInternal() { - return input; - } - - /** - * Check if this tokenizer has more tokens - * - * @return Whether or not this tokenizer has more tokens - */ - public boolean hasMoreTokens() { - return input.hasMoreTokens(); - } - - /** - * Return the next token from the tokenizer. - * - * Returns null if no more tokens are available - * - * @return The next token from the tokenizer - */ - public String nextToken() { - if (input.hasMoreTokens()) // Return the next available token - return input.nextToken(); - - // Return no token - return null; - } - - /** - * Convert this tokenizer into a list of strings - * - * @return This tokenizer, converted into a list of strings - */ - public IList toList() { - return toList((final String element) -> element); - } - - /** - * Convert the contents of this tokenizer into a list. Consumes all of - * the input from this tokenizer. - * - * @param - * The type of the converted tokens - * - * @param transformer - * The function to use to convert tokens. - * @return A list containing all of the converted tokens. - */ - public IList toList(final Function transformer) { - if (transformer == null) throw new NullPointerException("Transformer must not be null"); - - final IList returned = new FunctionalList<>(); - - // Add each token to the list after transforming it - forEachToken(token -> { - final E transformedToken = transformer.apply(token); - - returned.add(transformedToken); - }); - - return returned; - } - - @Override - public String toString() { - return String.format("FunctionalStringTokenizer [input=%s]", input); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/IList.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/IList.java deleted file mode 100644 index 28c09d0..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/IList.java +++ /dev/null @@ -1,416 +0,0 @@ -package bjc.utils.funcdata; - -import java.util.Comparator; -import java.util.Iterator; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collector; - -import bjc.utils.data.IPair; -import bjc.utils.functypes.ID; - -/** - * A wrapper over another list that provides functional operations over it. - * - * @author ben - * - * @param - * The type in this list - */ -public interface IList extends Iterable { - /** - * Add an item to this list - * - * @param item - * The item to add to this list. - * @return Whether the item was added to the list successfully. - */ - boolean add(ContainedType item); - - /** - * Add all of the elements in the provided list to this list - * - * @param items - * The list of items to add - * @return True if every item was successfully added to the list, false - * otherwise - */ - default boolean addAll(final IList items) { - return items.map(this::add).anyMatch(bl -> bl == false); - } - - /** - * Add all of the elements in the provided array to this list. - * - * @param items - * The array of items to add. - * - * @return True if every item was successfully added to the list, false - * otherwise. - */ - @SuppressWarnings("unchecked") - default boolean addAll(final ContainedType... items) { - boolean succ = true; - - for (final ContainedType item : items) { - final boolean addSucc = add(item); - - succ = succ ? addSucc : false; - } - - return succ; - } - - /** - * Check if all of the elements of this list match the specified - * predicate. - * - * @param matcher - * The predicate to use for checking. - * @return Whether all of the elements of the list match the specified - * predicate. - */ - boolean allMatch(Predicate matcher); - - /** - * Check if any of the elements in this list match the specified list. - * - * @param matcher - * The predicate to use for checking. - * @return Whether any element in the list matches the provided - * predicate. - */ - boolean anyMatch(Predicate matcher); - - /** - * Reduce the contents of this list using a collector - * - * @param - * The intermediate accumulation type - * @param - * The final, reduced type - * @param collector - * The collector to use for reduction - * @return The reduced list - */ - default ReducedType collect( - final Collector collector) { - final BiConsumer accumulator = collector.accumulator(); - - final StateType initial = collector.supplier().get(); - return reduceAux(initial, (value, state) -> { - accumulator.accept(state, value); - - return state; - }, collector.finisher()); - } - - /** - * Combine this list with another one into a new list and merge the - * results. - * - * Works sort of like a combined zip/map over resulting pairs. Does not - * change the underlying list. - * - * NOTE: The returned list will have the length of the shorter of this - * list and the combined one. - * - * @param - * The type of the second list - * @param - * The type of the combined list - * - * @param list - * The list to combine with - * @param combiner - * The function to use for combining element pairs. - * @return A new list containing the merged pairs of lists. - */ - IList combineWith(IList list, - BiFunction combiner); - - /** - * Check if the list contains the specified item - * - * @param item - * The item to see if it is contained - * @return Whether or not the specified item is in the list - */ - boolean contains(ContainedType item); - - /** - * Get the first element in the list - * - * @return The first element in this list. - */ - ContainedType first(); - - /** - * Apply a function to each member of the list, then flatten the - * results. - * - * Does not change the underlying list. - * - * @param - * The type of the flattened list - * - * @param expander - * The function to apply to each member of the list. - * @return A new list containing the flattened results of applying the - * provided function. - */ - IList flatMap(Function> expander); - - /** - * Apply a given action for each member of the list - * - * @param action - * The action to apply to each member of the list. - */ - @Override - void forEach(Consumer action); - - /** - * Apply a given function to each element in the list and its index. - * - * @param action - * The function to apply to each element in the list and - * its index. - */ - void forEachIndexed(BiConsumer action); - - /** - * Retrieve a value in the list by its index. - * - * @param index - * The index to retrieve a value from. - * @return The value at the specified index in the list. - */ - ContainedType getByIndex(int index); - - /** - * Retrieve a list containing all elements matching a predicate - * - * @param predicate - * The predicate to match by - * @return A list containing all elements that match the predicate - */ - IList getMatching(Predicate predicate); - - /** - * Retrieve the size of the wrapped list - * - * @return The size of the wrapped list - */ - int getSize(); - - /** - * Check if this list is empty. - * - * @return Whether or not this list is empty. - */ - boolean isEmpty(); - - /** - * Create a new list by applying the given function to each element in - * the list. - * - * Does not change the underlying list. - * - * @param - * The type of the transformed list - * - * @param transformer - * The function to apply to each element in the list - * @return A new list containing the mapped elements of this list. - */ - IList map(Function transformer); - - /** - * Zip two lists into a list of pairs - * - * @param - * The type of the second list - * - * @param list - * The list to use as the left side of the pair - * @return A list containing pairs of this element and the specified - * list - */ - IList> pairWith(IList list); - - /** - * Partition this list into a list of sublists - * - * @param partitionSize - * The size of elements to put into each one of the - * sublists - * @return A list partitioned into partitions of size nPerPart - */ - IList> partition(int partitionSize); - - /** - * Prepend an item to the list - * - * @param item - * The item to prepend to the list - */ - void prepend(ContainedType item); - - /** - * Prepend an array of items to the list. - * - * @param items - * The items to prepend to the list. - */ - @SuppressWarnings("unchecked") - default void prependAll(final ContainedType... items) { - for (final ContainedType item : items) { - prepend(item); - } - } - - /** - * Select a random item from the list, using a default random number - * generator - * - * @return A random item from the list - */ - default ContainedType randItem() { - return randItem(num -> (int) (Math.random() * num)); - } - - /** - * Select a random item from this list, using the provided random number - * generator. - * - * @param rnd - * The random number generator to use. - * @return A random element from this list. - */ - ContainedType randItem(Function rnd); - - /** - * Reduce this list to a single value, using a accumulative approach. - * - * @param - * The in-between type of the values - * @param - * The final value type - * - * @param initial - * The initial value of the accumulative state. - * @param accumulator - * The function to use to combine a list element with the - * accumulative state. - * @param transformer - * The function to use to convert the accumulative state - * into a final result. - * @return A single value condensed from this list and transformed into - * its final state. - */ - ReducedType reduceAux(StateType initial, - BiFunction accumulator, - Function transformer); - - /** - * Reduce this list to a single value, using a accumulative approach. - * - * @param - * The in-between type of the values. - * - * @param initial - * The initial value of the accumulative state. - * - * @param accumulator - * The function to use to combine a list element with the - * accumulative state. - * - * @return A single value condensed from this list. - */ - default StateType reduceAux(StateType initial, - BiFunction accumulator) { - return reduceAux(initial, accumulator, ID.id()); - } - - /** - * Remove all elements that match a given predicate - * - * @param predicate - * The predicate to use to determine elements to delete - * @return Whether there was anything that satisfied the predicate - */ - boolean removeIf(Predicate predicate); - - /** - * Remove all parameters that match a given parameter - * - * @param element - * The object to remove all matching copies of - */ - void removeMatching(ContainedType element); - - /** - * Reverse the contents of this list in place - */ - void reverse(); - - /** - * Perform a binary search for the specified key using the provided - * means of comparing elements. - * - * Since this IS a binary search, the list must have been sorted before - * hand. - * - * @param key - * The key to search for. - * @param comparator - * 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. - */ - ContainedType search(ContainedType key, Comparator comparator); - - /** - * Sort the elements of this list using the provided way of comparing - * elements. - * - * Does change the underlying list. - * - * @param comparator - * The way to compare elements for sorting. Pass null to - * use E's natural ordering - */ - void sort(Comparator comparator); - - /** - * Get the tail of this list (the list without the first element - * - * @return The list without the first element - */ - IList tail(); - - /** - * Convert this list into an array - * - * @param type - * The type of array to return - * @return The list, as an array - */ - ContainedType[] toArray(ContainedType[] type); - - /** - * Convert the list into a Iterable - * - * @return An iterable view onto the list - */ - Iterable toIterable(); - - @Override - default Iterator iterator() { - return toIterable().iterator(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/IMap.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/IMap.java deleted file mode 100644 index 0ee7375..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/IMap.java +++ /dev/null @@ -1,188 +0,0 @@ -package bjc.utils.funcdata; - -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; - -/** - * 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 interface IMap { - /** - * Execute an action for each entry in the map. - * - * @param action - * the action to execute for each entry in the map. - */ - void forEach(BiConsumer action); - - /** - * Perform an action for each key in the map. - * - * @param action - * The action to perform on each key in the map. - */ - default void forEachKey(final Consumer action) { - forEach((key, val) -> action.accept(key)); - } - - /** - * Perform an action for each value in the map. - * - * @param action - * The action to perform on each value in the map. - */ - default void forEachValue(final Consumer action) { - forEach((key, val) -> action.accept(val)); - } - - /** - * Check if this map contains the specified key. - * - * @param key - * The key to check. - * - * @return Whether or not the map contains the key. - */ - boolean containsKey(KeyType key); - - /** - * Get the value assigned to the given key. - * - * @param key - * The key to look for a value under. - * - * @return The value of the key. - */ - ValueType get(KeyType key); - - /** - * Get a value from the map, and return a default value if the key - * doesn't exist. - * - * @param key - * The key to attempt to retrieve. - * - * @param defaultValue - * The value to return if the key doesn't exist. - * - * @return The value associated with the key, or the default value if - * the key doesn't exist. - */ - default ValueType getOrDefault(final KeyType key, final ValueType defaultValue) { - try { - return get(key); - } catch (final IllegalArgumentException iaex) { - /* - * We don't care about this, because it indicates a key - * is missing. - */ - return defaultValue; - } - } - - /** - * 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. - * - * @throws UnsupportedOperationException - * if the map implementation doesn't support modifying - * the map - */ - ValueType put(KeyType key, ValueType val); - - /** - * Delete all the values in the map. - */ - default void clear() { - keyList().forEach(key -> remove(key)); - } - - /** - * Get the number of entries in this map. - * - * @return The number of entries in this map. - */ - default int size() { - return keyList().getSize(); - } - - /** - * 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. - */ - default IMap transform(final Function transformer) { - return new TransformedValueMap<>(this, transformer); - } - - /** - * Extends this map, creating a new map that will delegate queries to - * the map, but store any added values itself. - * - * @return An extended map. - */ - IMap extend(); - - /** - * Remove the value bound to the key. - * - * @param key - * The key to remove from the map. - * - * @return The previous value for the key in the map, or null if the key - * wasn't in the class. NOTE: Just because you received null, - * doesn't mean the map wasn't changed. It may mean that someone - * put a null value for that key into the map. - */ - ValueType remove(KeyType key); - - /** - * Get a list of all the keys in this map. - * - * @return A list of all the keys in this map. - */ - IList keyList(); - - /** - * Get a list of the values in this map. - * - * @return A list of values in this map. - */ - default IList valueList() { - final IList returns = new FunctionalList<>(); - - for (final KeyType key : keyList()) { - returns.add(get(key)); - } - - return returns; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/SentryList.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/SentryList.java deleted file mode 100644 index c322743..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/SentryList.java +++ /dev/null @@ -1,41 +0,0 @@ -package bjc.utils.funcdata; - -import java.util.List; - -/** - * A list that logs when items are inserted into it. - * - * @author bjculkin - * - * @param - * The type of item in the list. - */ -public class SentryList extends FunctionalList { - /** - * Create a new sentry list. - */ - public SentryList() { - super(); - } - - /** - * Create a new sentry list backed by an existing list. - * - * @param backing - * The backing list. - */ - public SentryList(final List backing) { - super(backing); - } - - @Override - public boolean add(final T item) { - final boolean val = super.add(item); - - if (val) { - System.out.println("Added item (" + item + ") to list"); - } - - return val; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/TransformedValueMap.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/TransformedValueMap.java deleted file mode 100644 index 0ca1fdc..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/TransformedValueMap.java +++ /dev/null @@ -1,102 +0,0 @@ -package bjc.utils.funcdata; - -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; - -/** - * A map that transforms values from one type to another - * - * @author ben - * - * @param - * The type of the map's keys - * @param - * The type of the map's values - * @param - * The type of the transformed values - */ -final class TransformedValueMap implements IMap { - private final IMap backing; - private final Function transformer; - - public TransformedValueMap(final IMap backingMap, - final Function transform) { - backing = backingMap; - transformer = transform; - } - - @Override - public void clear() { - backing.clear(); - } - - @Override - public boolean containsKey(final OldKey key) { - return backing.containsKey(key); - } - - @Override - public IMap extend() { - return new ExtendedMap<>(this, new FunctionalMap<>()); - } - - @Override - public void forEach(final BiConsumer action) { - backing.forEach((key, value) -> { - action.accept(key, transformer.apply(value)); - }); - } - - @Override - public void forEachKey(final Consumer action) { - backing.forEachKey(action); - } - - @Override - public void forEachValue(final Consumer action) { - backing.forEachValue(value -> { - action.accept(transformer.apply(value)); - }); - } - - @Override - public NewValue get(final OldKey key) { - return transformer.apply(backing.get(key)); - } - - @Override - public int size() { - return backing.size(); - } - - @Override - public IList keyList() { - return backing.keyList(); - } - - @Override - public IMap transform(final Function transform) { - return new TransformedValueMap<>(this, transform); - } - - @Override - public NewValue put(final OldKey key, final NewValue value) { - throw new UnsupportedOperationException("Can't add items to transformed map"); - } - - @Override - public NewValue remove(final OldKey key) { - return transformer.apply(backing.remove(key)); - } - - @Override - public String toString() { - return backing.toString(); - } - - @Override - public IList valueList() { - return backing.valueList().map(transformer); - } -} 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 deleted file mode 100644 index 8acd477..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTree.java +++ /dev/null @@ -1,221 +0,0 @@ -package bjc.utils.funcdata.bst; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.function.Predicate; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * A binary search tree, with some mild support for functional traversal. - * - * @author ben - * - * @param - * The data type stored in the node. - */ -public class BinarySearchTree { - /* - * The comparator for use in ordering items - */ - private final Comparator comparator; - - /* - * The current count of elements in the tree - */ - private int elementCount; - - /* - * The root element of the tree - */ - private ITreePart root; - - /** - * Create a new tree using the specified way to compare elements. - * - * @param cmp - * The thing to use for comparing elements - */ - public BinarySearchTree(final Comparator cmp) { - if (cmp == null) throw new NullPointerException("Comparator must not be null"); - - elementCount = 0; - comparator = cmp; - } - - /** - * Add a node to the binary search tree. - * - * @param element - * The data to add to the binary search tree. - */ - public void addNode(final T element) { - elementCount++; - - if (root == null) { - root = new BinarySearchTreeNode<>(element, null, null); - } else { - root.add(element, comparator); - } - } - - /** - * Check if an adjusted pivot falls with the bounds of a list - * - * @param elements - * The list to get bounds from - * @param pivot - * The pivot - * @param pivotAdjustment - * The distance from the pivot - * @return Whether the adjusted pivot is with the list - */ - private boolean adjustedPivotInBounds(final IList elements, final int pivot, final int pivotAdjustment) { - return pivot - pivotAdjustment >= 0 && pivot + pivotAdjustment < elements.getSize(); - } - - /** - * Balance the tree, and remove soft-deleted nodes for free. - * - * Takes O(N) time, but also O(N) space. - */ - public void balance() { - final IList elements = new FunctionalList<>(); - - // Add each element to the list in sorted order - root.forEach(TreeLinearizationMethod.INORDER, element -> elements.add(element)); - - // Clear the tree - root = null; - - // Set up the pivot and adjustment for readding elements - final int pivot = elements.getSize() / 2; - int pivotAdjustment = 0; - - // Add elements until there aren't any left - while (adjustedPivotInBounds(elements, pivot, pivotAdjustment)) { - if (root == null) { - // Create a new root element - root = new BinarySearchTreeNode<>(elements.getByIndex(pivot), null, null); - } else { - // Add the left and right elements in a balanced - // manner - root.add(elements.getByIndex(pivot + pivotAdjustment), comparator); - - root.add(elements.getByIndex(pivot - pivotAdjustment), comparator); - } - - // Increase the distance from the pivot - pivotAdjustment++; - } - - // Add any trailing unbalanced elements - if (pivot - pivotAdjustment >= 0) { - root.add(elements.getByIndex(pivot - pivotAdjustment), comparator); - } else if (pivot + pivotAdjustment < elements.getSize()) { - root.add(elements.getByIndex(pivot + pivotAdjustment), comparator); - } - } - - /** - * Soft-delete a node from the tree. - * - * Soft-deleted nodes stay in the tree until trim()/balance() is - * invoked, and are not included in traversals/finds. - * - * @param element - * The node to delete - */ - public void deleteNode(final T element) { - elementCount--; - - root.delete(element, comparator); - } - - /** - * Get the root of the tree. - * - * @return The root of the tree. - */ - public ITreePart getRoot() { - return root; - } - - /** - * Check if a node is in the tree - * - * @param element - * The node to check the presence of for the tree. - * @return Whether or not the node is in the tree. - */ - public boolean isInTree(final T element) { - return root.contains(element, comparator); - } - - /** - * Traverse the tree in a specified way until the function fails - * - * @param linearizationMethod - * The way to linearize the tree for traversal - * @param traversalPredicate - * The function to use until it fails - */ - public void traverse(final TreeLinearizationMethod linearizationMethod, final 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"); - - root.forEach(linearizationMethod, traversalPredicate); - } - - /** - * Remove all soft-deleted nodes from the tree. - */ - public void trim() { - final List nodes = new ArrayList<>(elementCount); - - // Add all non-soft deleted nodes to the tree in insertion order - traverse(TreeLinearizationMethod.PREORDER, node -> { - nodes.add(node); - return true; - }); - - // Clear the tree - root = null; - - // Add the nodes to the tree in the order they were inserted - nodes.forEach(node -> addNode(node)); - } - - @Override - public String toString() { - return String.format("BinarySearchTree [elementCount=%s, root='%s']", elementCount, root); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + elementCount; - result = prime * result + (root == null ? 0 : root.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof BinarySearchTree)) return false; - - final BinarySearchTree other = (BinarySearchTree) obj; - - if (elementCount != other.elementCount) return false; - if (root == null) { - if (other.root != null) return false; - } else if (!root.equals(other.root)) return false; - - return true; - } -} 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 deleted file mode 100644 index 8c4f3f0..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeLeaf.java +++ /dev/null @@ -1,119 +0,0 @@ -package bjc.utils.funcdata.bst; - -import java.util.Comparator; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; - -/** - * A leaf in a tree. - * - * @author ben - * - * @param - * The data stored in the tree. - */ -public class BinarySearchTreeLeaf implements ITreePart { - /** - * The data held in this tree leaf - */ - protected T data; - - /** - * Whether this node is soft-deleted or not - */ - protected boolean isDeleted; - - /** - * Create a new leaf holding the specified data. - * - * @param element - * The data for the leaf to hold. - */ - public BinarySearchTreeLeaf(final T element) { - data = element; - } - - @Override - public void add(final T element, final Comparator comparator) { - throw new IllegalArgumentException("Can't add to a leaf."); - } - - @Override - public E collapse(final Function leafTransformer, final BiFunction branchCollapser) { - if (leafTransformer == null) throw new NullPointerException("Transformer must not be null"); - - return leafTransformer.apply(data); - } - - @Override - public boolean contains(final T element, final Comparator comparator) { - return this.data.equals(element); - } - - @Override - public T data() { - return data; - } - - @Override - public void delete(final T element, final Comparator comparator) { - if (data.equals(element)) { - isDeleted = true; - } - } - - @Override - public boolean directedWalk(final DirectedWalkFunction treeWalker) { - if (treeWalker == null) throw new NullPointerException("Tree walker must not be null"); - - switch (treeWalker.walk(data)) { - case SUCCESS: - return true; - // We don't have any children to care about - case FAILURE: - case LEFT: - case RIGHT: - default: - return false; - } - } - - @Override - public boolean forEach(final TreeLinearizationMethod linearizationMethod, - final Predicate traversalPredicate) { - if (traversalPredicate == null) throw new NullPointerException("Predicate must not be null"); - - return traversalPredicate.test(data); - } - - @Override - public String toString() { - return String.format("BinarySearchTreeLeaf [data='%s', isDeleted=%s]", data, isDeleted); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (data == null ? 0 : data.hashCode()); - result = prime * result + (isDeleted ? 1231 : 1237); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof BinarySearchTreeLeaf)) return false; - - final BinarySearchTreeLeaf other = (BinarySearchTreeLeaf) obj; - - if (data == null) { - if (other.data != null) return false; - } else if (!data.equals(other.data)) return false; - if (isDeleted != other.isDeleted) return false; - - return true; - } -} 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 deleted file mode 100644 index 9f45c17..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeNode.java +++ /dev/null @@ -1,287 +0,0 @@ -package bjc.utils.funcdata.bst; - -import static bjc.utils.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.FAILURE; -import static bjc.utils.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.LEFT; -import static bjc.utils.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.RIGHT; -import static bjc.utils.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.SUCCESS; - -import java.util.Comparator; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; - -/** - * A binary node in a tree. - * - * @author ben - * - * @param - * The data type stored in the tree. - */ -public class BinarySearchTreeNode extends BinarySearchTreeLeaf { - /* - * The left child of this node - */ - private ITreePart left; - - /* - * The right child of this node - */ - private ITreePart right; - - /** - * Create a new node with the specified data and children. - * - * @param element - * The data to store in this node. - * @param lft - * The left child of this node. - * @param rght - * The right child of this node. - */ - public BinarySearchTreeNode(final T element, final ITreePart lft, final ITreePart rght) { - super(element); - this.left = lft; - this.right = rght; - } - - @Override - public void add(final T element, final Comparator comparator) { - if (comparator == null) throw new NullPointerException("Comparator must not be null"); - - switch (comparator.compare(data, element)) { - case -1: - if (left == null) { - left = new BinarySearchTreeNode<>(element, null, null); - } else { - left.add(element, comparator); - } - break; - case 0: - if (isDeleted) { - isDeleted = false; - } else throw new IllegalArgumentException("Can't add duplicate values"); - break; - case 1: - if (right == null) { - right = new BinarySearchTreeNode<>(element, null, null); - } else { - right.add(element, comparator); - } - break; - default: - throw new IllegalStateException("Error: Comparator yielded invalid value"); - } - } - - @Override - public E collapse(final Function nodeCollapser, final BiFunction branchCollapser) { - if (nodeCollapser == null || branchCollapser == null) - throw new NullPointerException("Collapser must not be null"); - - final E collapsedNode = nodeCollapser.apply(data); - - if (left != null) { - final E collapsedLeftBranch = left.collapse(nodeCollapser, branchCollapser); - - if (right != null) { - final E collapsedRightBranch = right.collapse(nodeCollapser, branchCollapser); - - final E collapsedBranches = branchCollapser.apply(collapsedLeftBranch, - collapsedRightBranch); - - return branchCollapser.apply(collapsedNode, collapsedBranches); - } - - return branchCollapser.apply(collapsedNode, collapsedLeftBranch); - } - - if (right != null) { - final E collapsedRightBranch = right.collapse(nodeCollapser, branchCollapser); - - return branchCollapser.apply(collapsedNode, collapsedRightBranch); - } - - return collapsedNode; - } - - @Override - public boolean contains(final T element, final Comparator comparator) { - if (comparator == null) throw new NullPointerException("Comparator must not be null"); - - return directedWalk(currentElement -> { - switch (comparator.compare(element, currentElement)) { - case -1: - return LEFT; - case 0: - return isDeleted ? FAILURE : SUCCESS; - case 1: - return RIGHT; - default: - return FAILURE; - } - }); - } - - @Override - public void delete(final T element, final Comparator comparator) { - if (comparator == null) throw new NullPointerException("Comparator must not be null"); - - directedWalk(currentElement -> { - switch (comparator.compare(data, element)) { - case -1: - return left == null ? FAILURE : LEFT; - case 0: - isDeleted = true; - return FAILURE; - case 1: - return right == null ? FAILURE : RIGHT; - default: - return FAILURE; - } - }); - } - - @Override - public boolean directedWalk(final DirectedWalkFunction treeWalker) { - if (treeWalker == null) throw new NullPointerException("Walker must not be null"); - - switch (treeWalker.walk(data)) { - case SUCCESS: - return true; - case LEFT: - return left.directedWalk(treeWalker); - case RIGHT: - return right.directedWalk(treeWalker); - case FAILURE: - return false; - default: - return false; - } - } - - @Override - public boolean forEach(final TreeLinearizationMethod linearizationMethod, - final 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, traversalPredicate); - case INORDER: - return inorderTraverse(linearizationMethod, traversalPredicate); - case POSTORDER: - return postorderTraverse(linearizationMethod, traversalPredicate); - default: - throw new IllegalArgumentException( - "Passed an incorrect TreeLinearizationMethod " + linearizationMethod + ". WAT"); - } - } - - private boolean inorderTraverse(final TreeLinearizationMethod linearizationMethod, - final Predicate traversalPredicate) { - if (!traverseLeftBranch(linearizationMethod, traversalPredicate)) return false; - - if (!traverseElement(traversalPredicate)) return false; - - if (!traverseRightBranch(linearizationMethod, traversalPredicate)) return false; - - return true; - } - - private boolean postorderTraverse(final TreeLinearizationMethod linearizationMethod, - final Predicate traversalPredicate) { - if (!traverseLeftBranch(linearizationMethod, traversalPredicate)) return false; - - if (!traverseRightBranch(linearizationMethod, traversalPredicate)) return false; - - if (!traverseElement(traversalPredicate)) return false; - - return true; - - } - - private boolean preorderTraverse(final TreeLinearizationMethod linearizationMethod, - final Predicate traversalPredicate) { - if (!traverseElement(traversalPredicate)) return false; - - if (!traverseLeftBranch(linearizationMethod, traversalPredicate)) return false; - - if (!traverseRightBranch(linearizationMethod, traversalPredicate)) return false; - - return true; - } - - private boolean traverseElement(final Predicate traversalPredicate) { - boolean nodeSuccesfullyTraversed; - - if (isDeleted) { - nodeSuccesfullyTraversed = true; - } else { - nodeSuccesfullyTraversed = traversalPredicate.test(data); - } - - return nodeSuccesfullyTraversed; - } - - private boolean traverseLeftBranch(final TreeLinearizationMethod linearizationMethod, - final Predicate traversalPredicate) { - boolean leftSuccesfullyTraversed; - - if (left == null) { - leftSuccesfullyTraversed = true; - } else { - leftSuccesfullyTraversed = left.forEach(linearizationMethod, traversalPredicate); - } - - return leftSuccesfullyTraversed; - } - - private boolean traverseRightBranch(final TreeLinearizationMethod linearizationMethod, - final Predicate traversalPredicate) { - boolean rightSuccesfullyTraversed; - - if (right == null) { - rightSuccesfullyTraversed = true; - } else { - rightSuccesfullyTraversed = right.forEach(linearizationMethod, traversalPredicate); - } - - return rightSuccesfullyTraversed; - } - - @Override - public String toString() { - return String.format("BinarySearchTreeNode [left='%s', right='%s']", left, right); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + (left == null ? 0 : left.hashCode()); - result = prime * result + (right == null ? 0 : right.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (!super.equals(obj)) return false; - if (!(obj instanceof BinarySearchTreeNode)) return false; - - final BinarySearchTreeNode other = (BinarySearchTreeNode) obj; - - if (left == null) { - if (other.left != null) return false; - } else if (!left.equals(other.left)) return false; - - if (right == null) { - if (other.right != null) return false; - } else if (!right.equals(other.right)) return false; - - return true; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/DirectedWalkFunction.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/DirectedWalkFunction.java deleted file mode 100644 index e11524a..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/DirectedWalkFunction.java +++ /dev/null @@ -1,49 +0,0 @@ -package bjc.utils.funcdata.bst; - -/** - * Represents a function for doing a directed walk of a binary tree. - * - * @author ben - * - * @param - * The type of element stored in the walked tree - */ -@FunctionalInterface -public interface DirectedWalkFunction { - /** - * Represents the results used to direct a walk in a binary tree. - * - * @author ben - * - */ - public enum DirectedWalkResult { - /** - * Specifies that the function has failed. - */ - FAILURE, - /** - * Specifies that the function wants to move left in the tree - * next. - */ - LEFT, - /** - * Specifies that the function wants to move right in the tree - * next. - */ - RIGHT, - /** - * Specifies that the function has succesfully completed - * - */ - SUCCESS - } - - /** - * Perform a directed walk on a node of a tree. - * - * @param element - * The data stored in the node currently being visited - * @return The way the function wants the walk to go next. - */ - public DirectedWalkResult walk(T element); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/ITreePart.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/ITreePart.java deleted file mode 100644 index 3aa8880..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/ITreePart.java +++ /dev/null @@ -1,96 +0,0 @@ -package bjc.utils.funcdata.bst; - -import java.util.Comparator; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; - -/** - * A interface for the fundamental things that want to be part of a tree. - * - * @author ben - * - * @param - * The data contained in this part of the tree. - */ -public interface ITreePart { - /** - * Add a element below this tree part somewhere. - * - * @param element - * The element to add below this tree part - * @param comparator - * The thing to use for comparing values to find where to - * insert the tree part. - */ - public void add(T element, Comparator comparator); - - /** - * Collapses this tree part into a single value. Does not change the - * underlying tree. - * - * @param - * The type of the final collapsed value - * - * @param nodeCollapser - * The function to use to transform data into mapped - * form. - * @param branchCollapser - * The function to use to collapse data in mapped form - * into a single value. - * @return A single value from collapsing the tree. - */ - public E collapse(Function nodeCollapser, BiFunction branchCollapser); - - /** - * Check if this tre part or below it contains the specified data item - * - * @param element - * The data item to look for. - * @param comparator - * The comparator to use to search for the data item - * @return Whether or not the given item is contained in this tree part - * or its children. - */ - public boolean contains(T element, Comparator comparator); - - /** - * Get the data associated with this tree part. - * - * @return The data associated with this tree part. - */ - public T data(); - - /** - * Remove the given node from this tree part and any of its children. - * - * @param element - * The data item to remove. - * @param comparator - * The comparator to use to search for the data item. - */ - public void delete(T element, Comparator comparator); - - /** - * Execute a directed walk through the tree. - * - * @param walker - * The function to use to direct the walk through the - * tree. - * @return Whether the directed walk finished successfully. - */ - public boolean directedWalk(DirectedWalkFunction walker); - - /** - * Execute a provided function for each element of tree it succesfully - * completes for - * - * @param linearizationMethod - * The way to linearize the tree for executing - * @param predicate - * The predicate to apply to each element, where it - * returning false terminates traversal early - * @return Whether the traversal finished succesfully - */ - public boolean forEach(TreeLinearizationMethod linearizationMethod, Predicate predicate); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/TreeLinearizationMethod.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/TreeLinearizationMethod.java deleted file mode 100644 index 0c83867..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/TreeLinearizationMethod.java +++ /dev/null @@ -1,25 +0,0 @@ -package bjc.utils.funcdata.bst; - -/** - * Represents the ways to linearize a tree for traversal. - * - * @author ben - * - */ -public enum TreeLinearizationMethod { - /** - * Visit the left side of this tree part, the tree part itself, and then - * the right part. - */ - INORDER, - /** - * Visit the left side of this tree part, the right side, and then the - * tree part itself. - */ - POSTORDER, - /** - * Visit the tree part itself, then the left side of tthis tree part and - * then the right part. - */ - PREORDER -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java deleted file mode 100644 index 13c1709..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java +++ /dev/null @@ -1,139 +0,0 @@ -package bjc.utils.funcdata.theory; - -import java.util.function.Function; - -/** - * A functor over a pair of heterogeneous types - * - * @author ben - * @param - * The type stored on the 'left' of the pair - * @param - * The type stored on the 'right' of the pair - * - */ -public interface Bifunctor { - /** - * Alias for functor mapping. - * - * @author EVE - * - * @param - * @param - * @param - * @param - */ - public interface BifunctorMap - extends Function, Bifunctor> { - - } - - /** - * Alias for left functor mapping. - * - * @author EVE - * - * @param - * @param - * @param - */ - public interface LeftBifunctorMap - extends BifunctorMap { - - } - - /** - * Alias for right functor mapping. - * - * @author EVE - * - * @param - * @param - * @param - */ - public interface RightBifunctorMap - extends BifunctorMap { - - } - - /** - * Lift a pair of functions to a single function that maps over both - * parts of a pair - * - * @param - * The old left type of the pair - * @param - * The old right type of the pair - * @param - * The new left type of the pair - * @param - * The new right type of the pair - * @param leftFunc - * The function that maps over the left of the pair - * @param rightFunc - * The function that maps over the right of the pair - * @return A function that maps over both parts of the pair - */ - public default BifunctorMap bimap( - final Function leftFunc, final Function rightFunc) { - final BifunctorMap bimappedFunc = (argPair) -> { - final LeftBifunctorMap leftMapper = argPair.fmapLeft(leftFunc); - - final Bifunctor leftMappedFunctor = leftMapper.apply(argPair); - final RightBifunctorMap rightMapper = leftMappedFunctor - .fmapRight(rightFunc); - - return rightMapper.apply(leftMappedFunctor); - }; - - return bimappedFunc; - } - - /** - * Lift a function to operate over the left part of this pair - * - * @param - * The old left type of the pair - * @param - * The old right type of the pair - * @param - * The new left type of the pair - * @param func - * The function to lift to work over the left side of the - * pair - * @return The function lifted to work over the left side of bifunctors - */ - public LeftBifunctorMap fmapLeft( - Function func); - - /** - * Lift a function to operate over the right part of this pair - * - * @param - * The old left type of the pair - * @param - * The old right type of the pair - * @param - * The new right type of the pair - * @param func - * The function to lift to work over the right side of - * the pair - * @return The function lifted to work over the right side of bifunctors - */ - public RightBifunctorMap fmapRight( - Function func); - - /** - * Get the value contained on the left of this bifunctor - * - * @return The value on the left side of this bifunctor - */ - public LeftType getLeft(); - - /** - * Get the value contained on the right of this bifunctor - * - * @return The value on the right of this bifunctor - */ - public RightType getRight(); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Functor.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Functor.java deleted file mode 100644 index 1c53284..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/theory/Functor.java +++ /dev/null @@ -1,39 +0,0 @@ -package bjc.utils.funcdata.theory; - -import java.util.function.Function; - -/** - * Represents a container or context some sort usually, but the precise - * definition is that it represents exactly what it is defined as - * - * @author ben - * @param - * The value inside the functor - */ -public interface Functor { - /** - * Converts a normal function to operate over values in a functor. - * - * N.B: Even though the type signature implies that you can apply the - * resulting function to any type of functor, it is only safe to call it - * on instances of the type of functor you called fmap on. - * - * @param - * The argument of the function - * @param - * The return type of the function - * @param func - * The function to convert - * @return The passed in function converted to work over a particular - * type of functors - */ - public Function, Functor> fmap( - Function func); - - /** - * Retrieve the thing inside this functor - * - * @return The thing inside this functor - */ - public ContainedType getValue(); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/functypes/ID.java b/BJC-Utils2/src/main/java/bjc/utils/functypes/ID.java deleted file mode 100644 index d3197e2..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/functypes/ID.java +++ /dev/null @@ -1,20 +0,0 @@ -package bjc.utils.functypes; - -import java.util.function.UnaryOperator; - -/** - * Identity function. - * - * @author bjculkin - * - */ -public class ID { - /** - * Create an identity function. - * - * @return A identity function. - */ - public static UnaryOperator id() { - return (x) -> x; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/functypes/ListFlattener.java b/BJC-Utils2/src/main/java/bjc/utils/functypes/ListFlattener.java deleted file mode 100644 index cfa0c8b..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/functypes/ListFlattener.java +++ /dev/null @@ -1,17 +0,0 @@ -package bjc.utils.functypes; - -import java.util.function.Function; - -import bjc.utils.funcdata.IList; - -/** - * A function that flattens a list. - * - * @author bjculkin - * - * @param - * The type of value in the list. - */ -public interface ListFlattener extends Function, S> { - -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/CollectorUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/CollectorUtils.java deleted file mode 100644 index a044bfd..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/CollectorUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -package bjc.utils.funcutils; - -import java.util.stream.Collector; - -import bjc.utils.data.IHolder; -import bjc.utils.data.IPair; - -/** - * Utilities for producing implementations of {@link Collector} - * - * @author ben - * - */ -public class CollectorUtils { - /** - * Create a collector that applies two collectors at once - * - * @param - * The type of the collection to collect from - * @param - * The intermediate type of the first collector - * @param - * The intermediate type of the second collector - * @param - * The final type of the first collector - * @param - * The final type of the second collector - * @param first - * The first collector to use - * @param second - * The second collector to use - * @return A collector that functions as mentioned above - */ - public static Collector>, IPair> compoundCollect( - final Collector first, - final Collector second) { - return new CompoundCollector<>(first, second); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/CompoundCollector.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/CompoundCollector.java deleted file mode 100644 index 35695bc..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/CompoundCollector.java +++ /dev/null @@ -1,89 +0,0 @@ -package bjc.utils.funcutils; - -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BinaryOperator; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collector; - -import bjc.utils.data.IHolder; -import bjc.utils.data.IPair; -import bjc.utils.data.Identity; -import bjc.utils.data.Pair; - -final class CompoundCollector - implements Collector>, IPair> { - - private final Set characteristicSet; - - private final Collector first; - private final Collector second; - - public CompoundCollector(final Collector first, - final Collector second) { - this.first = first; - this.second = second; - - characteristicSet = first.characteristics(); - characteristicSet.addAll(second.characteristics()); - } - - @Override - public BiConsumer>, InitialType> accumulator() { - final BiConsumer firstAccumulator = first.accumulator(); - final BiConsumer secondAccumulator = second.accumulator(); - - return (state, value) -> { - state.doWith(statePair -> { - statePair.doWith((left, right) -> { - firstAccumulator.accept(left, value); - secondAccumulator.accept(right, value); - }); - }); - }; - } - - @Override - public Set characteristics() { - return characteristicSet; - } - - @Override - public BinaryOperator>> combiner() { - final BinaryOperator firstCombiner = first.combiner(); - final BinaryOperator secondCombiner = second.combiner(); - - return (leftState, rightState) -> { - return leftState.unwrap(leftPair -> { - return rightState.transform(rightPair -> { - return leftPair.combine(rightPair, firstCombiner, secondCombiner); - }); - }); - }; - } - - @Override - public Function>, IPair> finisher() { - return state -> { - return state.unwrap(pair -> { - return pair.bind((left, right) -> { - final FinalType1 finalLeft = first.finisher().apply(left); - final FinalType2 finalRight = second.finisher().apply(right); - - return new Pair<>(finalLeft, finalRight); - }); - }); - }; - } - - @Override - public Supplier>> supplier() { - return () -> { - final AuxType1 initialLeft = first.supplier().get(); - final AuxType2 initialRight = second.supplier().get(); - - return new Identity<>(new Pair<>(initialLeft, initialRight)); - }; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/EnumUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/EnumUtils.java deleted file mode 100644 index e4c0bda..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/EnumUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -package bjc.utils.funcutils; - -import java.util.Random; -import java.util.function.Consumer; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * Utility methods on enums - * - * @author ben - * - */ -public class EnumUtils { - /** - * Do an action for a random number of enum values - * - * @param - * The type of the enum - * @param clasz - * The enum class - * @param nValues - * The number of values to execute the action on - * @param action - * The action to perform on random values - * @param rnd - * The source of randomness to use - */ - public static > void doForValues(final Class clasz, final int nValues, - final Consumer action, final Random rnd) { - final E[] enumValues = clasz.getEnumConstants(); - - final IList valueList = new FunctionalList<>(enumValues); - - final int randomValueCount = enumValues.length - nValues; - - for (int i = 0; i <= randomValueCount; i++) { - final E rDir = valueList.randItem(rnd::nextInt); - - valueList.removeMatching(rDir); - } - - valueList.forEach(action); - } - - /** - * Get a random value from an enum - * - * @param - * The type of the enum - * @param clasz - * The class of the enum - * @param rnd - * The random source to use - * @return A random value from the specified enum - */ - public static > E getRandomValue(final Class clasz, final Random rnd) { - final E[] enumValues = clasz.getEnumConstants(); - - return new FunctionalList<>(enumValues).randItem(rnd::nextInt); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/FileUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/FileUtils.java deleted file mode 100644 index 87199b1..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/FileUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -package bjc.utils.funcutils; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.function.BiPredicate; - -/** - * Utilities for doing things with files - * - * @author ben - * - */ -public class FileUtils { - /** - * Traverse a directory recursively. This is a depth-first traversal - * - * - * @param root - * The directory to start the traversal at - * @param predicate - * The predicate to determine whether or not to traverse - * a directory - * @param action - * The action to invoke upon each file in the directory. - * Returning true means to continue the traversal, - * returning false stops it - * @throws IOException - * if the walk throws an exception - * - * TODO If it becomes necessary, write another overload - * for this with all the buttons and knobs from - * walkFileTree - */ - public static void traverseDirectory(final Path root, final BiPredicate predicate, - final BiPredicate action) throws IOException { - Files.walkFileTree(root, new FunctionalFileVisitor(predicate, action)); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/FuncUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/FuncUtils.java deleted file mode 100644 index 9950add..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/FuncUtils.java +++ /dev/null @@ -1,76 +0,0 @@ -package bjc.utils.funcutils; - -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.UnaryOperator; - -/** - * Utility things for functions - * - * @author ben - * - */ -public class FuncUtils { - /** - * Convert a binary function into a unary function that returns a - * function - * - * @param - * The initial type of the function - * @param - * The intermediate type of the function - * @param - * The terminal type of the function - * @param func - * The function to transform - * @return The function transformed into a unary function returning a - * function - */ - public static Function> curry2(final BiFunction func) { - return arg1 -> arg2 -> { - return func.apply(arg1, arg2); - }; - } - - /** - * Do the specified action the specified number of times - * - * @param nTimes - * The number of times to do the action - * @param cons - * The action to perform - */ - public static void doTimes(final int nTimes, final Consumer cons) { - for (int i = 0; i < nTimes; i++) { - cons.accept(i); - } - } - - /** - * Return an operator that executes until it converges. - * - * @param op - * The operator to execute. - * @param maxTries - * The maximum amount of times to apply the function in an - * attempt to cause it to converge. - */ - public static UnaryOperator converge(final UnaryOperator op, final int maxTries) { - return (val) -> { - T newVal = op.apply(val); - T oldVal; - - int tries = 0; - - do { - oldVal = newVal; - newVal = op.apply(newVal); - - tries += 1; - } while(!newVal.equals(oldVal) && tries < maxTries); - - return newVal; - }; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java deleted file mode 100644 index db6c43b..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java +++ /dev/null @@ -1,36 +0,0 @@ -package bjc.utils.funcutils; - -import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.function.BiPredicate; - -/* - * Functional implementation of a file visitor. - */ -final class FunctionalFileVisitor extends SimpleFileVisitor { - private final BiPredicate predicate; - private final BiPredicate action; - - public FunctionalFileVisitor(final BiPredicate predicate, - final BiPredicate action) { - this.predicate = predicate; - this.action = action; - } - - @Override - public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException { - if (predicate.test(dir, attrs)) return FileVisitResult.CONTINUE; - - return FileVisitResult.SKIP_SUBTREE; - } - - @Override - public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { - if (action.test(file, attrs)) return FileVisitResult.CONTINUE; - - return FileVisitResult.TERMINATE; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/GroupPartIteration.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/GroupPartIteration.java deleted file mode 100644 index f3b2254..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/GroupPartIteration.java +++ /dev/null @@ -1,62 +0,0 @@ -package bjc.utils.funcutils; - -import java.util.function.Consumer; -import java.util.function.Function; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * Implements a single group partitioning pass on a list - * - * @author ben - * - * @param - * The type of element in the list being partitioned - */ -final class GroupPartIteration implements Consumer { - private final IList> returnedList; - - public IList currentPartition; - private final IList rejectedItems; - - private int numberInCurrentPartition; - private final int numberPerPartition; - - private final Function elementCounter; - - public GroupPartIteration(final IList> returned, final IList rejects, final int nPerPart, - final Function eleCount) { - this.returnedList = returned; - this.rejectedItems = rejects; - this.numberPerPartition = nPerPart; - this.elementCounter = eleCount; - - this.currentPartition = new FunctionalList<>(); - this.numberInCurrentPartition = 0; - } - - @Override - public void accept(final E value) { - final boolean shouldStartPartition = numberInCurrentPartition >= numberPerPartition; - - if (shouldStartPartition) { - returnedList.add(currentPartition); - - currentPartition = new FunctionalList<>(); - numberInCurrentPartition = 0; - } else { - final int currentElementCount = elementCounter.apply(value); - - final boolean shouldReject = numberInCurrentPartition - + currentElementCount >= numberPerPartition; - - if (shouldReject) { - rejectedItems.add(value); - } else { - currentPartition.add(value); - numberInCurrentPartition += currentElementCount; - } - } - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/IBuilder.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/IBuilder.java deleted file mode 100644 index a96a4d6..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/IBuilder.java +++ /dev/null @@ -1,31 +0,0 @@ -package bjc.utils.funcutils; - -/** - * Generic interface for objects that implement the builder pattern - * - * @author ben - * - * @param - * The type of object being built - */ -public interface IBuilder { - /** - * Build the object this builder is building - * - * @return The built object - * @throws IllegalStateException - * if the data in the builder cannot be built into its - * corresponding object at this point in time - */ - public E build(); - - /** - * Reset the state of this builder to its initial state - * - * @throws UnsupportedOperationException - * if the builder doesn't support resetting its state - */ - public default void reset() { - throw new UnsupportedOperationException("Builder doesn't support state resetting"); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/Isomorphism.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/Isomorphism.java deleted file mode 100644 index 2d3655e..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/Isomorphism.java +++ /dev/null @@ -1,60 +0,0 @@ -package bjc.utils.funcutils; - -import java.util.function.Function; - -/** - * A pair of functions to transform between a pair of types. - * - * @author bjculkin - * - * @param - * The source type of the isomorphism. - * - * @param - * The destination type of isomorphism. - * - */ -public class Isomorphism { - private Function toFunc; - private Function fromFunc; - - /** - * Create a new isomorphism. - * - * @param to - * The 'forward' function, from the source to the - * definition. - * - * @param from - * The 'backward' function, from the definition to the - * source. - */ - public Isomorphism(Function to, Function from) { - toFunc = to; - fromFunc = from; - } - - /** - * Apply the isomorphism forward. - * - * @param val - * The source value. - * - * @return The destination value. - */ - public D to(S val) { - return toFunc.apply(val); - } - - /** - * Apply the isomorphism backward. - * - * @param val - * The destination value. - * - * @return The source value. - */ - public S from(D val) { - return fromFunc.apply(val); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/LambdaLock.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/LambdaLock.java deleted file mode 100644 index 62c5d32..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/LambdaLock.java +++ /dev/null @@ -1,105 +0,0 @@ -package bjc.utils.funcutils; - -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.function.Supplier; - -/** - * A wrapper around a {@link ReadWriteLock} to ensure that the lock is used - * properly. - * - * @author EVE - * - */ -public class LambdaLock { - private final Lock readLock; - private final Lock writeLock; - - /** - * Create a new lambda-enabled lock around a new lock. - */ - public LambdaLock() { - this(new ReentrantReadWriteLock()); - } - - /** - * Create a new lambda-enabled lock. - * - * @param lck - * The lock to wrap. - */ - public LambdaLock(final ReadWriteLock lck) { - readLock = lck.readLock(); - writeLock = lck.writeLock(); - } - - /** - * Execute an action with the read lock taken. - * - * @param supp - * The action to call. - * - * @return The result of the action. - */ - public T read(final Supplier supp) { - readLock.lock(); - - try { - return supp.get(); - } finally { - readLock.unlock(); - } - } - - /** - * Execute an action with the write lock taken. - * - * @param supp - * The action to call. - * - * @return The result of the action. - */ - public T write(final Supplier supp) { - writeLock.lock(); - - try { - return supp.get(); - } finally { - writeLock.unlock(); - } - } - - /** - * Execute an action with the read lock taken. - * - * @param action - * The action to call. - * - */ - public void read(final Runnable action) { - readLock.lock(); - - try { - action.run(); - } finally { - readLock.unlock(); - } - } - - /** - * Execute an action with the write lock taken. - * - * @param action - * The action to call. - */ - public void write(final Runnable action) { - writeLock.lock(); - - try { - action.run(); - } finally { - writeLock.unlock(); - } - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java deleted file mode 100644 index c0daa1e..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java +++ /dev/null @@ -1,294 +0,0 @@ -package bjc.utils.funcutils; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.function.Function; -import java.util.function.Supplier; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * Utilities for manipulating FunctionalLists that don't belong in the class - * itself - * - * @author ben - * - */ -public class ListUtils { - private static final int MAX_NTRIESPART = 50; - - /** - * Collapse a string of tokens into a single string without adding any - * spaces - * - * @param input - * The list of tokens to collapse - * @return The collapsed string of tokens - */ - public static String collapseTokens(final IList input) { - if (input == null) throw new NullPointerException("Input must not be null"); - - return collapseTokens(input, ""); - } - - /** - * Collapse a string of tokens into a single string, adding the desired - * separator after each token - * - * @param input - * The list of tokens to collapse - * @param seperator - * The separator to use for separating tokens - * @return The collapsed string of tokens - */ - public static String collapseTokens(final IList input, final 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 { - final StringBuilder state = new StringBuilder(); - - int i = 1; - for (final String itm : input.toIterable()) { - state.append(itm); - - if (i != input.getSize()) { - state.append(seperator); - } - - i += 1; - } - - return state.toString(); - } - } - - /** - * Select a number of random items from the list without replacement - * - * @param - * The type of items to select - * @param list - * The list to select from - * @param number - * The number of items to selet - * @param rng - * A function that creates a random number from 0 to the - * desired number - * @return A new list containing the desired number of items randomly - * selected from the specified list without replacement - */ - - public static IList drawWithoutReplacement(final IList list, final int number, - final Function rng) { - final IList selected = new FunctionalList<>(new ArrayList<>(number)); - - final int total = list.getSize(); - - final Iterator itr = list.toIterable().iterator(); - E element = null; - - for (final int index = 0; itr.hasNext(); element = itr.next()) { - /* - * n - m - */ - final int winningChance = number - selected.getSize(); - - /* - * N - t - */ - final int totalChance = total - (index - 1); - - /* - * Probability of selecting the t+1'th element - */ - if (NumberUtils.isProbable(winningChance, totalChance, rng)) { - selected.add(element); - } - } - - return selected; - } - - /** - * Select a number of random items from the list, with replacement - * - * @param - * The type of items to select - * @param list - * The list to select from - * @param number - * The number of items to selet - * @param rng - * A function that creates a random number from 0 to the - * desired number - * @return A new list containing the desired number of items randomly - * selected from the specified list - */ - public static IList drawWithReplacement(final IList list, final int number, - final Function rng) { - final IList selected = new FunctionalList<>(new ArrayList<>(number)); - - for (int i = 0; i < number; i++) { - selected.add(list.randItem(rng)); - } - - return selected; - } - - /** - * Partition a list into a list of lists, where each element can count - * for more than one element in a partition - * - * @param - * The type of elements in the list to partition - * - * @param input - * The list to partition - * @param counter - * The function to determine the count for each element - * for - * @param partitionSize - * The number of elements to put in each partition - * - * @return A list partitioned according to the above rules - */ - public static IList> groupPartition(final IList input, final Function counter, - final int partitionSize) { - if (input == null) - throw new NullPointerException("Input list must not be null"); - else if (counter == null) - throw new NullPointerException("Counter must not be null"); - else if (partitionSize < 1 || partitionSize > input.getSize()) { - final String fmt = "%d is not a valid partition size. Must be between 1 and %d"; - final String msg = String.format(fmt, partitionSize, input.getSize()); - - throw new IllegalArgumentException(msg); - } - - /* - * List that holds our results - */ - final IList> returned = new FunctionalList<>(); - - /* - * List that holds elements rejected during current pass - */ - final IList rejected = new FunctionalList<>(); - - final GroupPartIteration it = new GroupPartIteration<>(returned, rejected, partitionSize, counter); - - /* - * Run up to a certain number of passes - */ - for (int numberOfIterations = 0; numberOfIterations < MAX_NTRIESPART - && !rejected.isEmpty(); numberOfIterations++) { - input.forEach(it); - - if (rejected.isEmpty()) { - /* - * Nothing was rejected, so we're done - */ - return returned; - } - } - - - final String fmt = "Heuristic (more than %d iterations of partitioning) detected an unpartitionable list. (%s)\nThe following elements were not partitioned: %s\nCurrent group in formation: %s\nPreviously formed groups: %s\n"; - - final String msg = String.format(fmt, MAX_NTRIESPART, input.toString(), rejected.toString(), it.currentPartition.toString(), returned.toString()); - - throw new IllegalArgumentException(msg); - } - - /** - * Merge the contents of a bunch of lists together into a single list - * - * @param - * The type of value in this lists - * @param lists - * The values in the lists to merge - * @return A list containing all the elements of the lists - */ - @SafeVarargs - public static IList mergeLists(final IList... lists) { - final IList returned = new FunctionalList<>(); - - for (final IList list : lists) { - for (final E itm : list.toIterable()) { - returned.add(itm); - } - } - - return returned; - } - - /** - * Pad the provided list out to the desired size - * - * @param - * The type of elements in the list - * @param list - * The list to pad out - * @param counter - * The function to count elements with - * @param size - * The desired size of the list - * @param padder - * The function to get elements to pad with - * @return The list, padded to the desired size - * @throws IllegalArgumentException - * if the list couldn't be padded to the desired size - */ - public static IList padList(final IList list, final Function counter, final int size, - final Supplier padder) { - int count = 0; - - final IList returned = new FunctionalList<>(); - - for (final E itm : list.toIterable()) { - count += counter.apply(itm); - - returned.add(itm); - } - - if (count % size != 0) { - /* - * We need to pad - */ - int needed = count % size; - int threshold = 0; - - while (needed > 0 && threshold <= MAX_NTRIESPART) { - final E val = padder.get(); - final int newCount = counter.apply(val); - - if (newCount <= needed) { - returned.add(val); - - threshold = 0; - - needed -= newCount; - } else { - threshold += 1; - } - } - - if (threshold > MAX_NTRIESPART) { - final String fmt = "Heuristic (more than %d iterations of attempting to pad) detected an unpaddable list. (%s)\nPartially padded list: %S"; - - final String msg = String.format(fmt, MAX_NTRIESPART, list.toString(), returned.toString()); - - throw new IllegalArgumentException(msg); - } - } - - return returned; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/NumberUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/NumberUtils.java deleted file mode 100644 index 770d3a5..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/NumberUtils.java +++ /dev/null @@ -1,69 +0,0 @@ -package bjc.utils.funcutils; - -import java.util.function.Function; - -/** - * Utility functions for dealing with numbers - * - * @author ben - * - */ -public class NumberUtils { - /** - * Compute the falling factorial of a number - * - * @param value - * The number to compute - * @param power - * The power to do the falling factorial for - * @return The falling factorial of the number to the power - */ - public static int fallingFactorial(final int value, final int power) { - if (power == 0) - return 1; - else if (power == 1) - return value; - else { - int result = 1; - - for (int currentSub = 0; currentSub < power + 1; currentSub++) { - result *= value - currentSub; - } - - return result; - } - } - - /** - * Evaluates a linear probability distribution - * - * @param winning - * The number of winning possibilities - * @param total - * The number of total possibilities - * @param rng - * The function to use to generate a random possibility - * @return Whether or not a random possibility was a winning one - */ - public static boolean isProbable(final int winning, final int total, final Function rng) { - return rng.apply(total) < winning; - } - - /** - * Check if a number is in an inclusive range. - * - * @param min - * The minimum value of the range. - * - * @param max - * The maximum value of the range. - * - * @param i - * The number to check. - * - * @return Whether the number is in the range. - */ - public static boolean between(final int min, final int max, final int i) { - return i >= min && i <= max; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/StringUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/StringUtils.java deleted file mode 100644 index 62f78f5..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/StringUtils.java +++ /dev/null @@ -1,196 +0,0 @@ -package bjc.utils.funcutils; - -import java.util.Deque; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.ibm.icu.text.BreakIterator; - -/** - * Utility methods for operations on strings - * - * @author ben - * - */ -public class StringUtils { - /** - * Check if a string consists only of one or more matches of a regular - * expression - * - * @param input - * The string to check - * @param rRegex - * The regex to see if the string only contains matches - * of - * @return Whether or not the string consists only of multiple matches - * of the provided regex - */ - public static boolean containsOnly(final String input, final String rRegex) { - if (input == null) - throw new NullPointerException("Input must not be null"); - else if (rRegex == null) throw new NullPointerException("Regex must not be null"); - - /* - * This regular expression is fairly simple. - * - * First, we match the beginning of the string. Then, we start a - * non-capturing group whose contents are the passed in regex. - * That group is then matched one or more times and the pattern - * matches to the end of the string - */ - return input.matches("\\A(?:" + rRegex + ")+\\Z"); - } - - /** - * Indent the string being built in a StringBuilder n levels - * - * @param builder - * The builder to indent in - * @param levels - * The number of levels to indent - */ - public static void indentNLevels(final StringBuilder builder, final int levels) { - for (int i = 0; i < levels; i++) { - builder.append("\t"); - } - } - - /** - * Print out a deque with a special case for easily showing a deque is - * empty - * - * @param - * The type in the deque - * @param queue - * The deque to print - * @return A string version of the deque, with allowance for an empty - * deque - */ - public static String printDeque(final Deque queue) { - return queue.isEmpty() ? "(none)" : queue.toString(); - } - - /** - * Converts a sequence to an English list. - * - * @param objects - * The sequence to convert to an English list. - * @param join - * The string to use for separating the last element from - * the rest. - * @param comma - * The string to use as a comma - * - * @return The sequence as an English list. - */ - public static String toEnglishList(final Object[] objects, final String join, final String comma) { - if (objects == null) throw new NullPointerException("Sequence must not be null"); - - final StringBuilder sb = new StringBuilder(); - - final String joiner = join; - final String coma = comma; - - switch (objects.length) { - case 0: - /* - * Empty list. - */ - break; - case 1: - /* - * One item. - */ - sb.append(objects[0].toString()); - break; - case 2: - /* - * Two items. - */ - sb.append(objects[0].toString()); - sb.append(" " + joiner + " "); - sb.append(objects[1].toString()); - break; - default: - /* - * Three or more items. - */ - for (int i = 0; i < objects.length - 1; i++) { - sb.append(objects[i].toString()); - sb.append(coma + " "); - } - /* - * Uncomment this to remove serial commas. - * - * int lc = sb.length() - 1; - * - * sb.delete(lc - coma.length(), lc); - */ - sb.append(joiner + " "); - sb.append(objects[objects.length - 1].toString()); - } - - return sb.toString(); - } - - /** - * Converts a sequence to an English list. - * - * @param objects - * The sequence to convert to an English list. - * @param join - * The string to use for separating the last element from - * the rest. - * - * @return The sequence as an English list. - */ - public static String toEnglishList(final Object[] objects, final String join) { - return toEnglishList(objects, join, ","); - } - - /** - * Converts a sequence to an English list. - * - * @param objects - * The sequence to convert to an English list. - * @param and - * Whether to use 'and' or 'or'. - * - * @return The sequence as an English list. - */ - public static String toEnglishList(final Object[] objects, final boolean and) { - if (and) - return toEnglishList(objects, "and"); - else return toEnglishList(objects, "or"); - } - - /** - * Count the number of graphemes in a string. - * - * @param value - * The string to check. - * - * @return The number of graphemes in the string. - */ - public static int graphemeCount(final String value) { - final BreakIterator it = BreakIterator.getCharacterInstance(); - it.setText(value); - - int count = 0; - while (it.next() != BreakIterator.DONE) { - count++; - } - - return count; - } - - public static int countMatches(final String value, final String pattern) { - Matcher mat = Pattern.compile(pattern).matcher(value); - - int num = 0; - while(mat.find()) - num += 1; - - return num; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/TreeUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/TreeUtils.java deleted file mode 100644 index dcd5738..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/TreeUtils.java +++ /dev/null @@ -1,56 +0,0 @@ -package bjc.utils.funcutils; - -import java.util.LinkedList; -import java.util.function.Predicate; - -import bjc.utils.data.ITree; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * Implements various utilities for trees. - * - * @author Benjamin Culkin - */ -public class TreeUtils { - /* - * Convert a tree into a list of outline nodes that match a certain - * path. - */ - public static IList> outlineTree(ITree tre, Predicate leafMarker) { - IList> paths = new FunctionalList<>(); - - LinkedList path = new LinkedList<>(); - path.add(tre.getHead()); - - tre.doForChildren((child) -> findPath(child, path, leafMarker, paths)); - - return paths; - } - - private static void findPath(ITree subtree, LinkedList path, Predicate leafMarker, IList> paths) { - if(subtree.getChildrenCount() == 0 && leafMarker.test(subtree.getHead())) { - /* - * We're at a matching leaf node. Add it. - */ - IList finalPath = new FunctionalList<>(); - - for(T ePath : path) { - finalPath.add(ePath); - } - - finalPath.add(subtree.getHead()); - - paths.add(finalPath); - } else { - /* - * Check the children of this node. - */ - path.add(subtree.getHead()); - - subtree.doForChildren((child) -> findPath(child, path, leafMarker, paths)); - - path.removeLast(); - } - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/TriConsumer.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/TriConsumer.java deleted file mode 100644 index f30386c..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/TriConsumer.java +++ /dev/null @@ -1,31 +0,0 @@ -package bjc.utils.funcutils; - -/** - * Consumer that takes three arguments. - * - * @author EVE - * - * @param - * Type of the first argument. - * @param - * Type of the second argument. - * @param - * Type of the third argument. - * - */ -@FunctionalInterface -public interface TriConsumer { - /** - * Perform the action. - * - * @param a - * The first parameter. - * - * @param b - * The second parameter. - * - * @param c - * The third parameter. - */ - public void accept(A a, B b, C c); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gen/RandomGrammar.java b/BJC-Utils2/src/main/java/bjc/utils/gen/RandomGrammar.java deleted file mode 100644 index 3de08d6..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gen/RandomGrammar.java +++ /dev/null @@ -1,69 +0,0 @@ -package bjc.utils.gen; - -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IList; - -/** - * A weighted grammar where all the rules have a equal chance of occuring. - * - * @author ben - * - * @param - * The type of grammar elements to use. - */ -public class RandomGrammar extends WeightedGrammar { - /** - * Create a new random grammar. - */ - public RandomGrammar() { - rules = new FunctionalMap<>(); - } - - /** - * Add cases to a specified rule. - * - * @param rule - * The name of the rule to add cases to. - * @param cases - * The cases to add for this rule. - */ - @SafeVarargs - public final void addCases(final E rule, final IList... cases) { - for (final IList currentCase : cases) { - super.addCase(rule, 1, currentCase); - } - } - - /** - * Create a rule with the specified name and cases. - * - * @param rule - * The name of the rule to add. - * @param cases - * The cases to add for this rule. - */ - @SafeVarargs - public final void makeRule(final E rule, final IList... cases) { - super.addRule(rule); - - for (final IList currentCase : cases) { - super.addCase(rule, 1, currentCase); - } - } - - /** - * Create a rule with the specified name and cases. - * - * @param rule - * The name of the rule to add. - * @param cases - * The cases to add for this rule. - */ - public void makeRule(final E rule, final IList> 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 deleted file mode 100644 index 7777ad8..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedGrammar.java +++ /dev/null @@ -1,573 +0,0 @@ -package bjc.utils.gen; - -import java.util.Random; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import bjc.utils.data.IPair; -import bjc.utils.data.Pair; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.IMap; - -/** - * A random grammar, where certain rules will come up more often than others. - * - * @author ben - * - * @param - * The values that make up sentences of this grammar. - */ -public class WeightedGrammar { - /** - * The initial rule of the grammar - */ - protected String initialRule; - - /** - * The rules currently in this grammar - */ - protected IMap>> rules; - - /** - * The random number generator used for random numbers - */ - private Random rng; - - /** - * All of the subgrammars of this grammar - */ - protected IMap> subgrammars; - - /** - * Rules that require special handling - */ - private IMap>> specialRules; - - /** - * Predicate for marking special tokens - */ - - private Predicate specialMarker; - - /** - * Action for special tokens - */ - private BiFunction, IList> specialAction; - - /** - * Create a new weighted grammar. - */ - public WeightedGrammar() { - rules = new FunctionalMap<>(); - subgrammars = new FunctionalMap<>(); - specialRules = new FunctionalMap<>(); - } - - /** - * Create a new weighted grammar that uses the specified source of - * randomness. - * - * @param source - * The source of randomness to use - */ - public WeightedGrammar(final Random source) { - this(); - - if (source == null) throw new NullPointerException("Source of randomness must be non-null"); - - rng = source; - } - - /** - * Configure the action to perform on special tokens. - * - * @param marker - * The marker to find special tokens. - * - * @param action - * The action to take on those tokens. - */ - public void configureSpecial(final Predicate marker, - final BiFunction, IList> action) { - specialMarker = marker; - specialAction = action; - } - - /** - * Adds a special rule to the grammar. - * - * @param ruleName - * The name of the special rule. - * - * @param cse - * The case for the rule. - */ - public void addSpecialRule(final E ruleName, final Supplier> cse) { - if (ruleName == null) - throw new NullPointerException("Rule name must not be null"); - else if (cse == null) throw new NullPointerException("Case must not be null"); - - specialRules.put(ruleName, cse); - } - - /** - * Add a case to an already existing 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(final E ruleName, final int probability, final IList 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); - } - - /** - * Add a alias for an existing subgrammar - * - * @param name - * The name of the subgrammar to alias - * @param alias - * The alias of the subgrammar - * @return Whether the alias was succesfully created - */ - public boolean addGrammarAlias(final E name, final 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; - - if (subgrammars.containsKey(name)) { - subgrammars.put(alias, subgrammars.get(name)); - return true; - } - - return false; - } - - /** - * Add a new rule with no cases. - * - * @param name - * The name of the rule to add. - * @return Whether or not the rule was successfully added. - */ - public boolean addRule(final E name) { - if (rng == null) { - rng = new Random(); - } - - if (name == null) throw new NullPointerException("Rule name must not be null"); - - return addRule(name, new WeightedRandom<>(rng)); - } - - /** - * Add a new rule with a set of cases. - * - * @param name - * The name of the rule to add. - * @param cases - * The set of cases for the rule. - * @return Whether or not the rule was succesfully added. - */ - public boolean addRule(final E name, final 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; - - rules.put(name, cases); - return true; - } - - /** - * Add a subgrammar. - * - * @param name - * The name of the subgrammar. - * @param subgrammar - * The subgrammar to add. - * @return Whether or not the subgrammar was succesfully added. - */ - public boolean addSubgrammar(final E name, final 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; - - subgrammars.put(name, subgrammar); - return true; - } - - /** - * Remove a rule with the specified name. - * - * @param name - * The name of the rule to remove. - */ - public void deleteRule(final E name) { - if (name == null) throw new NullPointerException("Rule name must not be null"); - - rules.remove(name); - } - - /** - * Remove a subgrammar with the specified name. - * - * @param name - * The name of the subgrammar to remove. - */ - public void deleteSubgrammar(final E name) { - if (name == null) throw new NullPointerException("Rule name must not be null"); - - subgrammars.remove(name); - } - - /** - * Generate a set of debug sentences for the specified rule. - * - * Only generates sentences one layer deep. - * - * @param ruleName - * The rule to test. - * @return A set of sentences generated by the specified rule. - */ - public IList> generateDebugValues(final E ruleName) { - if (ruleName == null) throw new NullPointerException("Rule name must not be null"); - - final IList> returnedList = new FunctionalList<>(); - - final WeightedRandom> ruleGenerator = rules.get(ruleName); - - for (int i = 0; i < 10; i++) { - returnedList.add(ruleGenerator.generateValue()); - } - - return returnedList; - } - - /** - * Generate a generic sentence from a initial rule. - * - * @param - * The type of the transformed output - * - * @param initRules - * The initial rule to start with. - * - * @param tokenTransformer - * The function to transform grammar output into - * something. - * - * @param spacer - * The spacer element to add in between output tokens. - * - * @return A randomly generated sentence from the specified initial - * rule. - */ - public IList generateGenericValues(final E initRules, final Function tokenTransformer, - final T spacer) { - if (initRules == 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"); - - final IList returnedList = new FunctionalList<>(); - - IList genRules = new FunctionalList<>(initRules); - - if (specialMarker != null) { - if (specialMarker.test(initRules)) { - genRules = specialAction.apply(initRules, this); - } - } - - for (final E initRule : genRules.toIterable()) { - if (specialRules.containsKey(initRule)) { - for (final E rulePart : specialRules.get(initRule).get().toIterable()) { - final Iterable generatedRuleParts = generateGenericValues(rulePart, - tokenTransformer, spacer).toIterable(); - - for (final T generatedRulePart : generatedRuleParts) { - returnedList.add(generatedRulePart); - returnedList.add(spacer); - } - } - } else if (subgrammars.containsKey(initRule)) { - final Iterable ruleParts = subgrammars.get(initRule) - .generateGenericValues(initRule, tokenTransformer, spacer).toIterable(); - - for (final T rulePart : ruleParts) { - returnedList.add(rulePart); - returnedList.add(spacer); - } - } else if (rules.containsKey(initRule)) { - final Iterable ruleParts = rules.get(initRule).generateValue().toIterable(); - - for (final E rulePart : ruleParts) { - final Iterable generatedRuleParts = generateGenericValues(rulePart, - tokenTransformer, spacer).toIterable(); - - for (final T generatedRulePart : generatedRuleParts) { - returnedList.add(generatedRulePart); - returnedList.add(spacer); - } - } - } else { - final T transformedToken = tokenTransformer.apply(initRule); - - if (transformedToken == null) - throw new NullPointerException("Transformer created null token"); - - returnedList.add(transformedToken); - returnedList.add(spacer); - } - } - - return returnedList; - } - - /** - * Generate a random list of grammar elements from a given initial rule. - * - * @param initRule - * The initial rule to start with. - * @param spacer - * The item to use to space the list. - * @return A list of random grammar elements generated by the specified - * rule. - */ - public IList generateListValues(final E initRule, final E spacer) { - final IList retList = generateGenericValues(initRule, strang -> strang, spacer); - - return retList; - } - - /** - * Get the initial rule of this grammar - * - * @return The initial rule of this grammar - */ - public String getInitialRule() { - return initialRule; - } - - /** - * Returns the number of rules in this grammar - * - * @return The number of rules in this grammar - */ - public int getRuleCount() { - return rules.size(); - } - - /** - * Returns a set containing all of the rules in this grammar - * - * @return The set of all rule names in this grammar - */ - public IList getRuleNames() { - final IList ruleNames = new FunctionalList<>(); - - ruleNames.addAll(rules.keyList()); - ruleNames.addAll(specialRules.keyList()); - - return ruleNames; - } - - /** - * Get the subgrammar with the specified name. - * - * @param name - * The name of the subgrammar to get. - * @return The subgrammar with the specified name. - */ - public WeightedGrammar getSubgrammar(final E name) { - if (name == null) throw new NullPointerException("Subgrammar name must not be null"); - - return subgrammars.get(name); - } - - /** - * Check if this grammar has an initial rule - * - * @return Whether or not this grammar has an initial rule - */ - public boolean hasInitialRule() { - return initialRule != null && !initialRule.equalsIgnoreCase(""); - } - - /** - * Check if this grammar has a given rule. - * - * @param ruleName - * The rule to check for. - * - * @return Whether or not the grammar has a rule by that name. - */ - public boolean hasRule(final E ruleName) { - return rules.containsKey(ruleName) || specialRules.containsKey(ruleName); - } - - /** - * Prefix a given rule with a token multiple times - * - * @param ruleName - * The name of the rule to prefix - * @param prefixToken - * The token to prefix to the rules - * @param additionalProbability - * The additional probability of the tokens - * @param numberOfTimes - * The number of times to prefix the token - */ - public void multiPrefixRule(final E ruleName, final E prefixToken, final int additionalProbability, - final 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."); - - final WeightedRandom> rule = rules.get(ruleName); - - final IList>> newResults = new FunctionalList<>(); - - rule.getValues().forEach((pair) -> { - final IList> newRule = new FunctionalList<>(); - - for (int i = 1; i <= numberOfTimes; i++) { - final IList newCase = pair.merge((left, right) -> { - final IList returnVal = new FunctionalList<>(); - - for (final E val : right.toIterable()) { - returnVal.add(val); - } - - return returnVal; - }); - - for (int j = 1; j <= i; j++) { - newCase.prepend(prefixToken); - } - - newRule.add(newCase); - } - - newRule.forEach((list) -> { - final Integer currentProb = pair.merge((left, right) -> left); - - newResults.add(new Pair<>(currentProb + additionalProbability, list)); - }); - }); - - newResults.forEach((pair) -> { - pair.doWith((left, right) -> { - addCase(ruleName, left, right); - }); - }); - } - - /** - * Create a series of alternatives for a rule by prefixing them with a - * given token - * - * @param additionalProbability - * The amount to adjust the probability by - * @param ruleName - * The name of the rule to prefix - * @param prefixToken - * The token to prefix to the rule - */ - public void prefixRule(final E ruleName, final E prefixToken, final 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"); - - final WeightedRandom> rule = rules.get(ruleName); - - final IList>> newResults = new FunctionalList<>(); - - rule.getValues().forEach((pair) -> { - final IList newCase = pair.merge((left, right) -> { - final IList returnVal = new FunctionalList<>(); - - for (final E val : right.toIterable()) { - returnVal.add(val); - } - - return returnVal; - }); - - newCase.prepend(prefixToken); - - newResults.add(new Pair<>(pair.merge((left, right) -> left) + additionalProbability, newCase)); - }); - - newResults.forEach((pair) -> pair.doWith((left, right) -> addCase(ruleName, left, right))); - } - - /** - * Set the initial rule of the graphic - * - * @param initRule - * The initial rule of this grammar - */ - public void setInitialRule(final String initRule) { - this.initialRule = initRule; - } - - /** - * Suffix a token to a rule - * - * @param ruleName - * The rule to suffix - * @param suffixToken - * The token to prefix to the rule - * @param additionalProbability - * Additional probability of the prefixed rule - */ - public void suffixRule(final E ruleName, final E suffixToken, final 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"); - - final WeightedRandom> rule = rules.get(ruleName); - - final IList>> newResults = new FunctionalList<>(); - - rule.getValues().forEach((par) -> { - final IList newCase = par.merge((left, right) -> { - final IList returnVal = new FunctionalList<>(); - - for (final E val : right.toIterable()) { - returnVal.add(val); - } - - return returnVal; - }); - - newCase.add(suffixToken); - - newResults.add(new Pair<>(par.merge((left, right) -> left) + additionalProbability, newCase)); - }); - - newResults.forEach((pair) -> pair.doWith((left, right) -> addCase(ruleName, left, right))); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedRandom.java b/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedRandom.java deleted file mode 100644 index 18225ef..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedRandom.java +++ /dev/null @@ -1,112 +0,0 @@ -package bjc.utils.gen; - -import java.util.Random; - -import bjc.utils.data.IHolder; -import bjc.utils.data.IPair; -import bjc.utils.data.Identity; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * Represents a random number generator where certain results are weighted more - * heavily than others. - * - * @author ben - * - * @param - * The type of values that are randomly selected. - */ -public class WeightedRandom { - /* - * The list of probabilities for each result - */ - private final IList probabilities; - - /* - * The list of possible results to pick from - */ - private final IList results; - - /* - * The source for any needed random numbers - */ - private final Random source; - - private int totalChance; - - /** - * Create a new weighted random generator with the specified source of - * randomness - * - * @param src - * The source of randomness to use. - */ - public WeightedRandom(final Random src) { - probabilities = new FunctionalList<>(); - results = new FunctionalList<>(); - - if (src == null) throw new NullPointerException("Source of randomness must not be null"); - - source = src; - } - - /** - * Add a probability for a specific result to be given. - * - * @param chance - * The chance to get this result. - * @param result - * The result to get when the chance comes up. - */ - public void addProbability(final int chance, final E result) { - probabilities.add(chance); - results.add(result); - - totalChance += chance; - } - - /** - * Generate a weighted random value. - * - * @return A random value selected in a weighted fashion. - */ - public E generateValue() { - final IHolder value = new Identity<>(source.nextInt(totalChance)); - final IHolder current = new Identity<>(); - final IHolder picked = new Identity<>(true); - - probabilities.forEachIndexed((index, probability) -> { - if (picked.unwrap(bool -> bool)) { - if (value.unwrap((number) -> number < probability)) { - current.transform((result) -> results.getByIndex(index)); - - picked.transform((bool) -> false); - } else { - value.transform((number) -> number - probability); - } - } - }); - - return current.unwrap((result) -> result); - } - - /** - * Return a list of values that can be generated by this generator - * - * @return A list of all the values that can be generated - */ - public IList getResults() { - return results; - } - - /** - * Return a list containing values that can be generated paired with the - * probability of those values being generated - * - * @return A list of pairs of values and value probabilities - */ - public IList> getValues() { - return probabilities.pairWith(results); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/graph/AdjacencyMap.java b/BJC-Utils2/src/main/java/bjc/utils/graph/AdjacencyMap.java deleted file mode 100644 index 446ab5b..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/graph/AdjacencyMap.java +++ /dev/null @@ -1,216 +0,0 @@ -package bjc.utils.graph; - -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.InputMismatchException; -import java.util.Scanner; - -import bjc.utils.data.IHolder; -import bjc.utils.data.Identity; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.IMap; -import bjc.utils.funcutils.FuncUtils; - -/** - * An adjacency map representing a graph - * - * @author ben - * - * @param - * The type of the nodes in the graph - */ -public class AdjacencyMap { - /** - * Create an adjacency map from a stream of text - * - * @param stream - * The stream of text to read in - * @return An adjacency map defined by the text - */ - public static AdjacencyMap fromStream(final InputStream stream) { - if (stream == null) throw new NullPointerException("Input source must not be null"); - - // Create the adjacency map - AdjacencyMap adjacency; - - try (Scanner input = new Scanner(stream)) { - input.useDelimiter("\n"); - - int vertexCount; - - final String possible = input.next(); - - try { - // First, read in number of vertices - vertexCount = Integer.parseInt(possible); - } catch (final NumberFormatException nfex) { - final InputMismatchException imex = new InputMismatchException( - "The first line must contain the number of vertices. " + possible - + " is not a valid number"); - - imex.initCause(nfex); - - throw imex; - } - - if (vertexCount <= 0) - throw new InputMismatchException("The number of vertices must be greater than 0"); - - final IList vertices = new FunctionalList<>(); - - FuncUtils.doTimes(vertexCount, (vertexNo) -> vertices.add(vertexNo)); - - adjacency = new AdjacencyMap<>(vertices); - - final IHolder row = new Identity<>(0); - - input.forEachRemaining((strang) -> { - readRow(adjacency, vertexCount, row, strang); - }); - } - - return adjacency; - } - - private static void readRow(final AdjacencyMap adjacency, final int vertexCount, - final IHolder row, final String strang) { - final String[] parts = strang.split(" "); - - if (parts.length != vertexCount) - throw new InputMismatchException("Must specify a weight for all " + vertexCount + " vertices"); - - int column = 0; - - for (final String part : parts) { - int weight; - - try { - weight = Integer.parseInt(part); - } catch (final NumberFormatException nfex) { - final InputMismatchException imex = new InputMismatchException( - "" + part + " is not a valid weight."); - - imex.initCause(nfex); - - throw imex; - } - - adjacency.setWeight(row.getValue(), column, weight); - - column++; - } - - row.transform((rowNumber) -> rowNumber + 1); - } - - /** - * The backing storage of the map - */ - private final IMap> adjacency = new FunctionalMap<>(); - - /** - * Create a new map from a set of vertices - * - * @param vertices - * The set of vertices to create a map from - */ - public AdjacencyMap(final IList vertices) { - if (vertices == null) throw new NullPointerException("Vertices must not be null"); - - vertices.forEach(vertex -> { - final IMap row = new FunctionalMap<>(); - - vertices.forEach(target -> { - row.put(target, 0); - }); - - adjacency.put(vertex, row); - }); - } - - /** - * Check if the graph is directed - * - * @return Whether or not the graph is directed - */ - public boolean isDirected() { - final IHolder result = new Identity<>(true); - - adjacency.forEach((sourceKey, sourceValue) -> { - sourceValue.forEach((targetKey, targetValue) -> { - final int inverseValue = adjacency.get(targetKey).get(sourceKey); - - if (targetValue != inverseValue) { - result.replace(false); - } - }); - }); - - return result.getValue(); - } - - /** - * Set the weight of an edge - * - * @param source - * The source node of the edge - * @param target - * The target node of the edge - * @param weight - * The weight of the edge - */ - public void setWeight(final T source, final T target, final int weight) { - if (source == null) - throw new NullPointerException("Source vertex must not be null"); - else if (target == null) throw new NullPointerException("Target vertex must not be null"); - - if (!adjacency.containsKey(source)) - throw new IllegalArgumentException("Source vertex " + source + " isn't present in map"); - else if (!adjacency.containsKey(target)) - throw new IllegalArgumentException("Target vertex " + target + " isn't present in map"); - - adjacency.get(source).put(target, weight); - } - - /** - * Convert this to a different graph representation - * - * @return The new representation of this graph - */ - public Graph toGraph() { - final Graph ret = new Graph<>(); - - adjacency.forEach((sourceKey, sourceValue) -> { - sourceValue.forEach((targetKey, targetValue) -> { - ret.addEdge(sourceKey, targetKey, targetValue, true); - }); - }); - - return ret; - } - - /** - * Convert an adjacency map back into a stream - * - * @param sink - * The stream to convert to - */ - public void toStream(final OutputStream sink) { - if (sink == null) throw new NullPointerException("Output source must not be null"); - - final PrintStream outputPrinter = new PrintStream(sink); - - adjacency.forEach((sourceKey, sourceValue) -> { - sourceValue.forEach((targetKey, targetValue) -> { - outputPrinter.printf("%d", targetValue); - }); - - outputPrinter.println(); - }); - - outputPrinter.close(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/graph/Edge.java b/BJC-Utils2/src/main/java/bjc/utils/graph/Edge.java deleted file mode 100644 index 0152e3d..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/graph/Edge.java +++ /dev/null @@ -1,112 +0,0 @@ -package bjc.utils.graph; - -/** - * An edge in a weighted graph - * - * @author ben - * - * @param - * The type of the nodes in the graph - */ -public class Edge { - /* - * The distance from initial to terminal node - */ - private final int distance; - - /* - * The initial and terminal nodes of this edge - */ - private final T source, target; - - /** - * Create a new edge with set parameters - * - * @param initial - * The initial node of the edge - * @param terminal - * The terminal node of the edge - * @param distance - * The distance between initial and terminal edge - */ - public Edge(final T initial, final T terminal, final int distance) { - if (initial == null) - throw new NullPointerException("Initial node must not be null"); - else if (terminal == null) throw new NullPointerException("Terminal node must not be null"); - - this.source = initial; - this.target = terminal; - this.distance = distance; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) - return true; - else if (obj == null) - return false; - else if (getClass() != obj.getClass()) - return false; - else { - final 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; - } - } - - /** - * Get the distance in this edge - * - * @return The distance between the initial and terminal nodes of this - * edge - */ - public int getDistance() { - return distance; - } - - /** - * Get the initial node of an edge - * - * @return The initial node of this edge - */ - public T getSource() { - return source; - } - - /** - * Get the target node of an edge - * - * @return The target node of this edge - */ - public T getTarget() { - return target; - } - - @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; - } - - @Override - public String toString() { - return " first vertex " + source + " to vertex " + target + " with distance: " + distance; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/graph/Graph.java b/BJC-Utils2/src/main/java/bjc/utils/graph/Graph.java deleted file mode 100644 index 280a7f5..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/graph/Graph.java +++ /dev/null @@ -1,267 +0,0 @@ -package bjc.utils.graph; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.PriorityQueue; -import java.util.Queue; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BiPredicate; - -import bjc.utils.data.IHolder; -import bjc.utils.data.Identity; -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.IMap; - -/** - * A directed weighted graph, where the vertices have some arbitrary label - * - * @author ben - * - * @param - * The label for vertices - */ -public class Graph { - /** - * Create a graph from a list of edges - * - * @param - * The type of data stored in the edges - * - * @param edges - * The list of edges to build from - * @return A graph built from the provided edge-list - */ - public static Graph fromEdgeList(final List> edges) { - final Graph g = new Graph<>(); - - edges.forEach(edge -> { - g.addEdge(edge.getSource(), edge.getTarget(), edge.getDistance(), true); - }); - - return g; - } - - /** - * The backing representation of the graph - */ - private final IMap> backing; - - /** - * Create a new graph - */ - public Graph() { - backing = new FunctionalMap<>(); - } - - /** - * Add a edge to the graph - * - * @param source - * The source vertex for this edge - * @param target - * The target vertex for this edge - * @param distance - * The distance from the source vertex to the target - * vertex - * @param directed - * Whether or not - */ - public void addEdge(final T source, final T target, final int distance, final boolean directed) { - // Can't add edges with a null source or target - if (source == null) - throw new NullPointerException("The source vertex cannot be null"); - else if (target == null) throw new NullPointerException("The target vertex cannot be null"); - - // Initialize adjacency list for vertices if necessary - if (!backing.containsKey(source)) { - backing.put(source, new FunctionalMap()); - } - - // Add the edge to the graph - backing.get(source).put(target, distance); - - // Handle possible directed edges - if (!directed) { - if (!backing.containsKey(target)) { - backing.put(target, new FunctionalMap()); - } - - backing.get(target).put(source, distance); - } - } - - /** - * Execute an action for all edges of a specific vertex matching - * conditions - * - * @param source - * The vertex to test edges for - * @param matcher - * The conditions an edge must match - * @param action - * The action to execute for matching edges - */ - public void forAllEdgesMatchingAt(final T source, final BiPredicate matcher, - final BiConsumer action) { - if (matcher == null) - throw new NullPointerException("Matcher must not be null"); - else if (action == null) throw new NullPointerException("Action must not be null"); - - getEdges(source).forEach((target, weight) -> { - if (matcher.test(target, weight)) { - action.accept(target, weight); - } - }); - } - - /** - * Get all the edges that begin at a particular source vertex - * - * @param source - * The vertex to use as a source - * @return All of the edges with the specified vertex as a source - */ - public IMap getEdges(final T source) { - // Can't find edges for a null source - if (source == null) - throw new NullPointerException("The source cannot be null."); - else if (!backing.containsKey(source)) - throw new IllegalArgumentException("Vertex " + source + " is not in graph"); - - return backing.get(source); - } - - /** - * Get the initial vertex of the graph - * - * @return The initial vertex of the graph - */ - public T getInitial() { - return backing.keyList().first(); - } - - /** - * Uses Prim's algorothm to calculate a MST for the graph. - * - * If the graph is non-connected, this will lead to unpredictable - * results. - * - * @return a list of edges that constitute the MST - */ - public List> getMinimumSpanningTree() { - // Set of all of the currently available edges - final Queue> available = new PriorityQueue<>(10, - (left, right) -> left.getDistance() - right.getDistance()); - - // The MST of the graph - final List> minimums = new ArrayList<>(); - - // The set of all of the visited vertices. - final Set visited = new HashSet<>(); - - // Start at the initial vertex and visit it - final IHolder source = new Identity<>(getInitial()); - - visited.add(source.getValue()); - - // Make sure we visit all the nodes - while (visited.size() != getVertexCount()) { - // Grab all edges adjacent to the provided edge - - forAllEdgesMatchingAt(source.getValue(), (target, weight) -> { - return !visited.contains(target); - }, (target, weight) -> { - final T vert = source.unwrap(vertex -> vertex); - - available.add(new Edge<>(vert, target, weight)); - }); - - // Get the edge with the minimum distance - final IHolder> minimum = new Identity<>(available.poll()); - - // Only consider edges where we haven't visited the - // target of - // the edge - while (visited.contains(minimum.getValue().getTarget())) { - minimum.transform((edge) -> available.poll()); - } - - // Add it to our MST - minimums.add(minimum.getValue()); - - // Advance to the next node - source.transform((vertex) -> minimum.unwrap(edge -> edge.getTarget())); - - // Visit this node - visited.add(source.getValue()); - } - - return minimums; - } - - /** - * Get the count of the vertices in this graph - * - * @return A count of the vertices in this graph - */ - public int getVertexCount() { - return backing.size(); - } - - /** - * Get all of the vertices in this graph. - * - * @return A unmodifiable set of all the vertices in the graph. - */ - public IList getVertices() { - return backing.keyList(); - } - - /** - * Remove the edge starting at the source and ending at the target - * - * @param source - * The source vertex for the edge - * @param target - * The target vertex for the edge - */ - public void removeEdge(final T source, final T target) { - // Can't remove things w/ null vertices - if (source == null) - throw new NullPointerException("The source vertex cannot be null"); - else if (target == null) throw new NullPointerException("The target vertex cannot be null"); - - // Can't remove if one vertice doesn't exists - if (!backing.containsKey(source)) - throw new NoSuchElementException("vertex " + source + " does not exist."); - - if (!backing.containsKey(target)) - throw new NoSuchElementException("vertex " + target + " does not exist."); - - backing.get(source).remove(target); - - // Uncomment this to turn the graph undirected - // graph.get(target).remove(source); - } - - /** - * Convert a graph into a adjacency map/matrix - * - * @return A adjacency map representing this graph - */ - public AdjacencyMap toAdjacencyMap() { - final AdjacencyMap adjacency = new AdjacencyMap<>(backing.keyList()); - - backing.forEach((sourceKey, sourceValue) -> { - sourceValue.forEach((targetKey, targetValue) -> { - adjacency.setWeight(sourceKey, targetKey, targetValue); - }); - }); - - return adjacency; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/ExtensionFileFilter.java b/BJC-Utils2/src/main/java/bjc/utils/gui/ExtensionFileFilter.java deleted file mode 100644 index 7c487eb..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/ExtensionFileFilter.java +++ /dev/null @@ -1,56 +0,0 @@ -package bjc.utils.gui; - -import java.io.File; -import java.util.List; - -import javax.swing.filechooser.FileFilter; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * A file filter based on extensions. - * - * Built for Swing. - * - * @author ben - * - */ -public class ExtensionFileFilter extends FileFilter { - /** - * The list holding all filtered extensions - */ - private final IList extensions; - - /** - * Create a new filter only showing files with the specified extensions. - * - * @param exts - * The extensions to show in this filter. - */ - public ExtensionFileFilter(final List exts) { - extensions = new FunctionalList<>(exts); - } - - /** - * Create a new filter only showing files with the specified extensions. - * - * @param exts - * The extensions to show in this filter. - */ - public ExtensionFileFilter(final String... exts) { - extensions = new FunctionalList<>(exts); - } - - @Override - public boolean accept(final File pathname) { - if (pathname == null) throw new NullPointerException("Pathname must not be null"); - - return extensions.anyMatch(pathname.getName()::endsWith); - } - - @Override - public String getDescription() { - return extensions.toString(); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleDialogs.java b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleDialogs.java deleted file mode 100644 index 59eb1c3..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleDialogs.java +++ /dev/null @@ -1,269 +0,0 @@ -package bjc.utils.gui; - -import java.awt.Component; -import java.awt.Frame; -import java.util.function.Function; -import java.util.function.Predicate; - -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; - -import bjc.utils.gui.layout.VLayout; - -/** - * Utility class for getting simple input from the user. - * - * @author ben - * - */ -public class SimpleDialogs { - /** - * Get a bounded integer from the user. - * - * @param parent - * The parent component for the dialogs. - * @param title - * The title for the dialogs. - * @param prompt - * The prompt to tell the user what to enter. - * @param lowerBound - * The lower integer bound to accept. - * @param upperBound - * The upper integer bound to accept. - * @return A int within the specified bounds. - */ - public static int getBoundedInt(final Component parent, final String title, final String prompt, - final int lowerBound, final int upperBound) { - return getValue(parent, title, prompt, (strang) -> { - try { - final int value = Integer.parseInt(strang); - - return value < upperBound && value > lowerBound; - } catch (final NumberFormatException nfex) { - // We don't care about the specifics of the - // exception, just - // that this value isn't good - return false; - } - }, Integer::parseInt); - } - - /** - * Asks the user to pick an option from a series of choices. - * - * @param - * The type of choices for the user to pick - * - * @param parent - * The parent frame for this dialog - * @param title - * The title of this dialog - * @param question - * The question being asked - * @param choices - * The available choices for the question - * @return The choice the user picked, or null if they didn't pick one - */ - @SuppressWarnings("unchecked") - public static E getChoice(final Frame parent, final String title, final String question, - final 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"); - - final JDialog chooser = new JDialog(parent, title, true); - chooser.setLayout(new VLayout(2)); - - final JPanel questionPane = new JPanel(); - - final JLabel questionText = new JLabel(question); - final JComboBox questionChoices = new JComboBox<>(choices); - - questionPane.add(questionText); - questionPane.add(questionChoices); - - final JPanel buttonPane = new JPanel(); - - final JButton okButton = new JButton("Ok"); - final JButton cancelButton = new JButton("Cancel"); - - okButton.addActionListener((event) -> chooser.dispose()); - cancelButton.addActionListener((event) -> chooser.dispose()); - - buttonPane.add(cancelButton); - buttonPane.add(okButton); - - chooser.add(questionPane); - chooser.add(buttonPane); - - chooser.pack(); - chooser.setVisible(true); - - return (E) questionChoices.getSelectedItem(); - } - - /** - * Get a integer from the user - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for dialogs. - * @param prompt - * The prompt to tell the user what to enter. - * @return A int. - */ - public static int getInt(final Component parent, final String title, final String prompt) { - return getValue(parent, title, prompt, strang -> { - try { - Integer.parseInt(strang); - return true; - } catch (final NumberFormatException nfex) { - // We don't care about this exception, just mark - // the value - // as not good - return false; - } - }, Integer::parseInt); - } - - /** - * Get a string from the user - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for the dialogs. - * @param prompt - * The prompt to tell the user what to enter. - * @return A string. - */ - public static String getString(final Component parent, final String title, final 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); - } - - /** - * Get a value parsable from a string from the user. - * - * @param - * The type of the value parsed from the string - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for dialogs. - * @param prompt - * The prompt to tell the user what to enter. - * @param validator - * A predicate to determine if a input is valid. - * @param transformer - * The function to transform the string into a value. - * @return The value parsed from a string. - */ - public static E getValue(final Component parent, final String title, final String prompt, - final Predicate validator, final Function transformer) { - if (validator == null) - throw new NullPointerException("Validator must not be null"); - else if (transformer == null) throw new NullPointerException("Transformer must not be null"); - - String input = getString(parent, title, prompt); - - while (!validator.test(input)) { - showError(parent, "I/O Error", "Please enter a valid value"); - - input = getString(parent, title, prompt); - } - - return transformer.apply(input); - } - - /** - * Get a whole number from the user. - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for dialogs. - * @param prompt - * The prompt to tell the user what to enter. - * @return A whole number. - */ - public static int getWhole(final Component parent, final String title, final String prompt) { - return getBoundedInt(parent, title, prompt, 0, Integer.MAX_VALUE); - } - - /** - * Ask the user a Yes/No question. - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for dialogs. - * @param question - * The question to ask the user. - * @return True if the user said yes, false otherwise. - */ - public static boolean getYesNo(final Component parent, final String title, final 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"); - - final int result = JOptionPane.showConfirmDialog(parent, question, title, JOptionPane.YES_NO_OPTION); - - return result == JOptionPane.YES_OPTION ? true : false; - } - - /** - * Show a error message to the user - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for dialogs. - * @param message - * The error to show the user. - */ - public static void showError(final Component parent, final String title, final String message) { - 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 (message == null) throw new NullPointerException("Error message must not be null"); - - JOptionPane.showMessageDialog(parent, message, title, JOptionPane.ERROR_MESSAGE); - } - - /** - * Show an informative message to the user - * - * @param parent - * The parent for this dialog - * @param title - * Show the title for this dialog - * @param message - * Show the message for this dialog - */ - public static void showMessage(final Component parent, final String title, final String message) { - 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 (message == null) throw new NullPointerException("Message must not be null"); - - JOptionPane.showMessageDialog(parent, title, message, JOptionPane.INFORMATION_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 deleted file mode 100644 index 7da0bd8..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleFileChooser.java +++ /dev/null @@ -1,198 +0,0 @@ -package bjc.utils.gui; - -import java.awt.Component; -import java.io.File; - -import javax.swing.JFileChooser; - -import bjc.utils.exceptions.FileNotChosenException; - -/** - * Utility class for easily prompting user for files. - * - * Built for Swing. - * - * @author ben - * - */ -public class SimpleFileChooser { - private static File doOpenFile(final Component parent, final String title, final JFileChooser files) { - if (title == null) throw new NullPointerException("Title must not be null"); - - files.setDialogTitle(title); - - boolean success = false; - - while (!success) { - try { - maybeDoOpenFile(parent, files); - - success = true; - } catch (final FileNotChosenException fncx) { - // We don't care about specifics - SimpleDialogs.showError(parent, "I/O Error", "Please pick a file to open"); - } - } - - return files.getSelectedFile(); - } - - private static File doSaveFile(final Component parent, final String title, final JFileChooser files) { - if (title == null) throw new NullPointerException("Title must not be null"); - - files.setDialogTitle(title); - - final boolean success = false; - - while (!success) { - try { - maybeDoSaveFile(parent, files); - - return files.getSelectedFile(); - } catch (final FileNotChosenException fncex) { - // We don't care about specifics - SimpleDialogs.showError(parent, "I/O Error", "Please pick a file to save to"); - } - } - } - - /** - * Prompt the user with a "Open File..." dialog. Keeps prompting them - * until they pick a file. - * - * @param parent - * The component to use as the parent for the dialog. - * @param title - * The title of the dialog to prompt with. - * @return The file the user has chosen. - */ - public static File getOpenFile(final Component parent, final String title) { - final JFileChooser files = new JFileChooser(); - - return doOpenFile(parent, title, files); - } - - /** - * Prompt the user with a "Open File..." dialog. Keeps prompting them - * until they pick a file. - * - * @param parent - * The component to use as the parent for the dialog. - * @param title - * The title of the dialog to prompt with. - * @param extensions - * The list of file extensions the file should have. - * @return The file the user has chosen. - */ - public static File getOpenFile(final Component parent, final String title, final String... extensions) { - final JFileChooser files = new JFileChooser(); - - files.addChoosableFileFilter(new ExtensionFileFilter(extensions)); - - return doOpenFile(parent, title, files); - } - - /** - * Prompt the user with a "Save File..." dialog. - * - * @param parent - * The component to use as the parent for the dialog. - * @param title - * The title of the dialog to prompt with. - * @return The file the user chose. - */ - public static File getSaveFile(final Component parent, final String title) { - final JFileChooser files = new JFileChooser(); - - return doSaveFile(parent, title, files); - } - - /** - * Prompt the user with a "Save File..." dialog. - * - * @param parent - * The component to use as the parent for the dialog. - * @param title - * The title of the dialog to prompt with. - * @param extensions - * The extensions of the files the user can choose. - * @return The file the user chose. - */ - public static File getSaveFile(final Component parent, final String title, final String... extensions) { - final JFileChooser files = new JFileChooser(); - - files.addChoosableFileFilter(new ExtensionFileFilter(extensions)); - - return doSaveFile(parent, title, files); - } - - private static void maybeDoOpenFile(final Component parent, final 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"); - - final int result = files.showSaveDialog(parent); - - if (result != JFileChooser.APPROVE_OPTION) throw new FileNotChosenException(); - } - - private static void maybeDoSaveFile(final Component parent, final 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"); - - final int result = files.showSaveDialog(parent); - - if (result != JFileChooser.APPROVE_OPTION) throw new FileNotChosenException(); - } - - /** - * Prompt the user with a "Open File..." dialog. - * - * @param parent - * The component to use as the parent for the dialog. - * @param title - * The title of the dialog to prompt with. - * @return The file if the user chose one or null if they didn't. - */ - public static File maybeOpenFile(final Component parent, final String title) { - if (title == null) throw new NullPointerException("Title must not be null"); - - final JFileChooser files = new JFileChooser(); - files.setDialogTitle(title); - - try { - maybeDoOpenFile(parent, files); - } catch (final FileNotChosenException fncex) { - // We don't care about specifics - } - - return files.getSelectedFile(); - } - - /** - * Prompt the user with a "Save File..." dialog. - * - * @param parent - * The component to use as the parent for the dialog. - * @param title - * The title of the dialog to prompt with. - * @return The file if the user chose one or null if they didn't. - */ - public static File maybeSaveFile(final Component parent, final String title) { - if (title == null) throw new NullPointerException("Title must not be null"); - - final JFileChooser files = new JFileChooser(); - files.setDialogTitle(title); - - try { - maybeDoSaveFile(parent, files); - } catch (final FileNotChosenException fncex) { - // We don't care about specifics - } - - return files.getSelectedFile(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleInternalDialogs.java b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleInternalDialogs.java deleted file mode 100644 index 5237557..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleInternalDialogs.java +++ /dev/null @@ -1,208 +0,0 @@ -package bjc.utils.gui; - -import java.awt.Component; -import java.util.function.Function; -import java.util.function.Predicate; - -import javax.swing.JOptionPane; - -/** - * Utility class for getting simple input from the user. - * - * Modified to work with JDesktopPanes - * - * @author ben - * - */ -public class SimpleInternalDialogs { - /** - * Get a bounded integer from the user. - * - * @param parent - * The parent component for the dialogs. - * @param title - * The title for the dialogs. - * @param prompt - * The prompt to tell the user what to enter. - * @param lowerBound - * The lower integer bound to accept. - * @param upperBound - * The upper integer bound to accept. - * @return A int within the specified bounds. - */ - public static int getBoundedInt(final Component parent, final String title, final String prompt, - final int lowerBound, final int upperBound) { - return getValue(parent, title, prompt, (strang) -> { - try { - final int value = Integer.parseInt(strang); - - return value < upperBound && value > lowerBound; - } catch (final NumberFormatException nfex) { - // We don't care about the specifics of the - // exception, just - // that this value isn't good - return false; - } - }, Integer::parseInt); - } - - /** - * Get a integer from the user - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for dialogs. - * @param prompt - * The prompt to tell the user what to enter. - * @return A int. - */ - public static int getInt(final Component parent, final String title, final String prompt) { - return getValue(parent, title, prompt, strang -> { - try { - Integer.parseInt(strang); - return true; - } catch (final NumberFormatException nfex) { - // We don't care about this exception, just mark - // the value - // as not good - return false; - } - }, Integer::parseInt); - } - - /** - * Get a string from the user - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for the dialogs. - * @param prompt - * The prompt to tell the user what to enter. - * @return A string. - */ - public static String getString(final Component parent, final String title, final 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.showInternalInputDialog(parent, prompt, title, JOptionPane.QUESTION_MESSAGE); - } - - /** - * Get a value parsable from a string from the user. - * - * @param - * The type of the value parsed from the string - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for dialogs. - * @param prompt - * The prompt to tell the user what to enter. - * @param validator - * A predicate to determine if a input is valid. - * @param transformer - * The function to transform the string into a value. - * @return The value parsed from a string. - */ - public static E getValue(final Component parent, final String title, final String prompt, - final Predicate validator, final Function transformer) { - if (validator == null) - throw new NullPointerException("Validator must not be null"); - else if (transformer == null) throw new NullPointerException("Transformer must not be null"); - - String strang = getString(parent, title, prompt); - - while (!validator.test(strang)) { - showError(parent, "I/O Error", "Please enter a valid value"); - - strang = getString(parent, title, prompt); - } - - return transformer.apply(strang); - } - - /** - * Get a whole number from the user. - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for dialogs. - * @param prompt - * The prompt to tell the user what to enter. - * @return A whole number. - */ - public static int getWhole(final Component parent, final String title, final String prompt) { - return getBoundedInt(parent, title, prompt, 0, Integer.MAX_VALUE); - } - - /** - * Ask the user a Yes/No question. - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for dialogs. - * @param question - * The question to ask the user. - * @return True if the user said yes, false otherwise. - */ - public static boolean getYesNo(final Component parent, final String title, final 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"); - - final int result = JOptionPane.showInternalConfirmDialog(parent, question, title, - JOptionPane.YES_NO_OPTION); - - return result == JOptionPane.YES_OPTION ? true : false; - } - - /** - * Show a error message to the user - * - * @param parent - * The parent component for dialogs. - * @param title - * The title for dialogs. - * @param message - * The error to show the user. - */ - public static void showError(final Component parent, final String title, final String message) { - 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 (message == null) throw new NullPointerException("Error message must not be null"); - - JOptionPane.showInternalMessageDialog(parent, message, title, JOptionPane.ERROR_MESSAGE); - } - - /** - * Show an informative message to the user - * - * @param parent - * The parent for this dialog - * @param title - * Show the title for this dialog - * @param message - * Show the message for this dialog - */ - public static void showMessage(final Component parent, final String title, final String message) { - 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 (message == null) throw new NullPointerException("Message must not be null"); - - JOptionPane.showInternalMessageDialog(parent, title, message, JOptionPane.INFORMATION_MESSAGE); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleInternalFrame.java b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleInternalFrame.java deleted file mode 100644 index afb498e..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleInternalFrame.java +++ /dev/null @@ -1,40 +0,0 @@ -package bjc.utils.gui; - -import javax.swing.JInternalFrame; - -/** - * A simple internal frame class - * - * @author ben - * - */ -public class SimpleInternalFrame extends JInternalFrame { - private static final long serialVersionUID = -2966801321260716617L; - - /** - * Create a new blank internal frame - */ - public SimpleInternalFrame() { - super(); - } - - /** - * Create a new blank internal frame with a specific title - * - * @param title - * The title of the internal frame - */ - public SimpleInternalFrame(final String title) { - super(title); - } - - protected void setupFrame() { - setSize(320, 240); - - setResizable(true); - - setClosable(true); - setMaximizable(true); - setIconifiable(true); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleJList.java b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleJList.java deleted file mode 100644 index 411d0db..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleJList.java +++ /dev/null @@ -1,49 +0,0 @@ -package bjc.utils.gui; - -import javax.swing.DefaultListModel; -import javax.swing.JList; -import javax.swing.ListModel; - -/** - * Utility class for making JLists and their models. - * - * @author ben - * - */ -public class SimpleJList { - /** - * Create a new JList from a given list. - * - * @param - * The type of data in the JList - * - * @param source - * The list to populate the JList with. - * @return A JList populated with the elements from ls. - */ - public static JList buildFromList(final Iterable source) { - if (source == null) throw new NullPointerException("Source must not be null"); - - return new JList<>(buildModel(source)); - } - - /** - * Create a new list model from a given list. - * - * @param - * The type of data in the list model - * - * @param source - * The list to fill the list model from. - * @return A list model populated with the elements from ls. - */ - public static ListModel buildModel(final Iterable source) { - if (source == null) throw new NullPointerException("Source must not be null"); - - final DefaultListModel defaultModel = new DefaultListModel<>(); - - source.forEach(defaultModel::addElement); - - return defaultModel; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleTitledBorder.java b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleTitledBorder.java deleted file mode 100644 index 9b01507..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleTitledBorder.java +++ /dev/null @@ -1,25 +0,0 @@ -package bjc.utils.gui; - -import javax.swing.border.EtchedBorder; -import javax.swing.border.TitledBorder; - -/** - * A simple border with a title attached to it. - * - * @author ben - * - */ -public class SimpleTitledBorder extends TitledBorder { - // Version ID for serialization - private static final long serialVersionUID = -5655969079949148487L; - - /** - * Create a new border with the specified title. - * - * @param title - * The title for the border. - */ - public SimpleTitledBorder(final String title) { - super(new EtchedBorder(), title); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/TextAreaOutputStream.java b/BJC-Utils2/src/main/java/bjc/utils/gui/TextAreaOutputStream.java deleted file mode 100644 index fbc58ed..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/TextAreaOutputStream.java +++ /dev/null @@ -1,35 +0,0 @@ -package bjc.utils.gui; - -import java.io.IOException; -import java.io.OutputStream; - -import javax.swing.JTextArea; - -/** - * An output stream that prints to a JTextArea - * - * @author epr - * @author Levente S\u00e1ntha (lsantha@users.sourceforge.net) - */ -public class TextAreaOutputStream extends OutputStream { - private final JTextArea textArea; - - /** - * Create a new output stream attached to a textarea - * - * @param console - * The textarea to write to - */ - public TextAreaOutputStream(final JTextArea console) { - this.textArea = console; - } - - @Override - public void write(final int b) throws IOException { - textArea.append("" + (char) b); - - if (b == '\n') { - textArea.repaint(); - } - } -} 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 deleted file mode 100644 index eb60ae2..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -package bjc.utils.gui.awt; - -import java.io.File; -import java.io.FilenameFilter; -import java.util.List; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * Filter a set of filenames by extension. - * - * Built for AWT - * - * @author ben - * - */ -public class ExtensionFileFilter implements FilenameFilter { - /** - * The list of extensions to filter - */ - private final IList extensions; - - /** - * Create a new filter only showing files with the specified extensions. - * - * @param exts - * The extensions to show in this filter. - */ - public ExtensionFileFilter(final List exts) { - if (exts == null) throw new NullPointerException("Extensions must not be null"); - - extensions = new FunctionalList<>(exts); - } - - /** - * Create a new filter only showing files with the specified extensions. - * - * @param exts - * The extensions to show in this filter. - */ - public ExtensionFileFilter(final String... exts) { - extensions = new FunctionalList<>(exts); - } - - @Override - public boolean accept(final File directory, final String name) { - return extensions.anyMatch(name::endsWith); - } -} \ No newline at end of file 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 deleted file mode 100644 index 77a4a59..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java +++ /dev/null @@ -1,144 +0,0 @@ -package bjc.utils.gui.awt; - -import java.awt.FileDialog; -import java.awt.Frame; -import java.io.File; -import java.io.FilenameFilter; - -import bjc.utils.gui.SimpleDialogs; - -/** - * A simple way to get the user to pick a file - * - * Built for AWT. - * - * @author ben - * - */ -public class SimpleFileDialog { - /** - * Prompt the user to pick a file to open - * - * @param parent - * The parent of the file picker - * @param title - * The title of the file picker - * @return The file the user picked - */ - public static File getOpenFile(final Frame parent, final String title) { - return getOpenFile(parent, title, (String[]) null); - } - - /** - * Prompt the user to pick a file to open - * - * @param parent - * The parent of the file picker - * @param title - * The title of the file picker - * @param extensions - * The extensions to accept as valid - * @return The file the user picked - */ - public static File getOpenFile(final Frame parent, final String title, final 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"); - - final FileDialog chooser = new FileDialog(parent, title, FileDialog.LOAD); - - if (extensions != null) { - final FilenameFilter filter = new ExtensionFileFilter(extensions); - chooser.setFilenameFilter(filter); - } - - chooser.setVisible(true); - - while (chooser.getFile() == null) { - SimpleDialogs.showError(parent, "File I/O Error", "Please choose a file to open."); - chooser.setVisible(true); - } - - return chooser.getFiles()[0]; - } - - /** - * Prompt the user to pick a file to open - * - * @param parent - * The parent of the file picker - * @param title - * The title of the file picker - * @param extensions - * The extensions to accept as valid - * @return The file the user picked - */ - public static File[] getOpenFiles(final Frame parent, final String title, final 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"); - - final FileDialog chooser = new FileDialog(parent, title, FileDialog.LOAD); - - if (extensions != null) { - final FilenameFilter filter = new ExtensionFileFilter(extensions); - chooser.setFilenameFilter(filter); - } - - chooser.setMultipleMode(true); - chooser.setVisible(true); - - while (chooser.getFile() == null) { - SimpleDialogs.showError(parent, "File I/O Error", "Please choose a file to open."); - chooser.setVisible(true); - } - - return chooser.getFiles(); - } - - /** - * Prompt the user to pick a file to save - * - * @param parent - * The parent of the file picker - * @param title - * The title of the file picker - * @return The file the user picked - */ - public static File getSaveFile(final Frame parent, final String title) { - return getSaveFile(parent, title, (String[]) null); - } - - /** - * Prompt the user to pick a file to save - * - * @param parent - * The parent of the file picker - * @param title - * The title of the file picker - * @param extensions - * The extensions to accept as valid - * @return The file the user picked - */ - public static File getSaveFile(final Frame parent, final String title, final 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"); - - final FileDialog chooser = new FileDialog(parent, title, FileDialog.SAVE); - - if (extensions != null) { - final FilenameFilter filter = new ExtensionFileFilter(extensions); - chooser.setFilenameFilter(filter); - } - - chooser.setVisible(true); - - while (chooser.getFile() == null) { - SimpleDialogs.showError(parent, "File I/O Error", "Please choose a file to save to."); - chooser.setVisible(true); - } - - return chooser.getFiles()[0]; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/layout/AutosizeLayout.java b/BJC-Utils2/src/main/java/bjc/utils/gui/layout/AutosizeLayout.java deleted file mode 100644 index 6f384f2..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/layout/AutosizeLayout.java +++ /dev/null @@ -1,22 +0,0 @@ -package bjc.utils.gui.layout; - -import java.awt.GridLayout; - -/** - * A layout that simply holds one component that it auto-resizes whenever it is - * resized. - * - * @author ben - * - */ -public class AutosizeLayout extends GridLayout { - // Version id for serialization - private static final long serialVersionUID = -2495693595953396924L; - - /** - * Create a new auto-size layout. - */ - public AutosizeLayout() { - super(1, 1); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/layout/HLayout.java b/BJC-Utils2/src/main/java/bjc/utils/gui/layout/HLayout.java deleted file mode 100644 index 4ed1661..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/layout/HLayout.java +++ /dev/null @@ -1,25 +0,0 @@ -package bjc.utils.gui.layout; - -import java.awt.GridLayout; - -/** - * A layout manager that lays out its components horizontally, evenly sizing - * them. - * - * @author ben - * - */ -public class HLayout extends GridLayout { - // Version ID for serialization - private static final long serialVersionUID = 1244964456966270026L; - - /** - * Create a new horizontal layout with the specified number of columns. - * - * @param columns - * The number of columns in this layout. - */ - public HLayout(final int columns) { - super(1, columns); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/layout/VLayout.java b/BJC-Utils2/src/main/java/bjc/utils/gui/layout/VLayout.java deleted file mode 100644 index 6993365..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/layout/VLayout.java +++ /dev/null @@ -1,25 +0,0 @@ -package bjc.utils.gui.layout; - -import java.awt.GridLayout; - -/** - * A layout that lays out its components vertically, evenly sharing space among - * them. - * - * @author ben - * - */ -public class VLayout extends GridLayout { - // Version ID for serializations - private static final long serialVersionUID = -6417962941602322663L; - - /** - * Create a new vertical layout with the specified number of rows. - * - * @param rows - * The number of rows. - */ - public VLayout(final int rows) { - super(rows, 1); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/DropdownListPanel.java b/BJC-Utils2/src/main/java/bjc/utils/gui/panels/DropdownListPanel.java deleted file mode 100644 index 4f71d38..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/DropdownListPanel.java +++ /dev/null @@ -1,73 +0,0 @@ -package bjc.utils.gui.panels; - -import java.awt.BorderLayout; - -import javax.swing.DefaultListModel; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JList; -import javax.swing.JPanel; -import javax.swing.ListSelectionModel; - -import bjc.utils.funcdata.IList; -import bjc.utils.gui.layout.AutosizeLayout; -import bjc.utils.gui.layout.HLayout; - -/** - * A panel that allows you to select choices from a dropdown list - * - * @author ben - * - */ -public class DropdownListPanel extends JPanel { - private static final long serialVersionUID = 2719963952350133541L; - - /** - * Create a new dropdown list panel - * - * @param - * The type of items in the dropdown list - * @param type - * The label of the type of items in the list - * @param model - * The model to put items into - * @param choices - * The items to choose from - */ - public DropdownListPanel(final String type, final DefaultListModel model, final IList choices) { - setLayout(new AutosizeLayout()); - - final JPanel itemInputPanel = new JPanel(); - itemInputPanel.setLayout(new BorderLayout()); - - final JPanel addItemPanel = new JPanel(); - addItemPanel.setLayout(new HLayout(2)); - - final JComboBox addItemBox = new JComboBox<>(); - choices.forEach(addItemBox::addItem); - - final JButton addItemButton = new JButton("Add " + type); - - addItemPanel.add(addItemBox); - addItemPanel.add(addItemButton); - - final JList itemList = new JList<>(model); - itemList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - final JButton removeItemButton = new JButton("Remove " + type); - - addItemButton.addActionListener((ev) -> { - model.addElement(addItemBox.getItemAt(addItemBox.getSelectedIndex())); - }); - - removeItemButton.addActionListener((ev) -> { - model.remove(itemList.getSelectedIndex()); - }); - - itemInputPanel.add(addItemPanel, BorderLayout.PAGE_START); - itemInputPanel.add(itemList, BorderLayout.CENTER); - itemInputPanel.add(removeItemButton, BorderLayout.PAGE_END); - - add(itemInputPanel); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/FormattedInputPanel.java b/BJC-Utils2/src/main/java/bjc/utils/gui/panels/FormattedInputPanel.java deleted file mode 100644 index 2cecf0c..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/FormattedInputPanel.java +++ /dev/null @@ -1,66 +0,0 @@ -package bjc.utils.gui.panels; - -import java.util.function.Consumer; - -import javax.swing.JFormattedTextField; -import javax.swing.JFormattedTextField.AbstractFormatter; -import javax.swing.JLabel; -import javax.swing.JPanel; - -import bjc.utils.gui.layout.HLayout; - -/** - * A simple panel allowing for input of a single formatted value - * - * @author ben - * - * @param - * The type of value being formatted - */ -public class FormattedInputPanel extends JPanel { - private static final long serialVersionUID = 5232016563558588031L; - - private final JFormattedTextField field; - - /** - * Create a new formatted input panel - * - * @param label - * The label for this panel - * @param length - * The length of this panel - * @param formatter - * The formatter to use for input - * @param reciever - * The action to call whenever the value changes - */ - @SuppressWarnings("unchecked") - public FormattedInputPanel(final String label, final int length, final AbstractFormatter formatter, - final Consumer reciever) { - setLayout(new HLayout(2)); - - final JLabel lab = new JLabel(label); - field = new JFormattedTextField(formatter); - - field.setColumns(length); - field.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT); - field.addPropertyChangeListener("value", (event) -> { - // This is safe, because InputVal should be the type of - // whatever object the formatter is returning - reciever.accept((InputVal) field.getValue()); - }); - - add(lab); - add(field); - } - - /** - * Reset the value in this panel to a specified value - * - * @param value - * The value to set the panel to - */ - public void resetValues(final InputVal value) { - field.setValue(value); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/HolderOutputPanel.java b/BJC-Utils2/src/main/java/bjc/utils/gui/panels/HolderOutputPanel.java deleted file mode 100644 index 653dace..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/HolderOutputPanel.java +++ /dev/null @@ -1,79 +0,0 @@ -package bjc.utils.gui.panels; - -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.Timer; - -import bjc.utils.data.IHolder; -import bjc.utils.gui.layout.HLayout; - -/** - * A panel that outputs a value bound to a {@link IHolder} - * - * @author ben - * - */ -public class HolderOutputPanel extends JPanel { - private static final long serialVersionUID = 166573313903782080L; - - private Timer updater; - private final JLabel value; - private final int nDelay; - private final IHolder val; - - /** - * Create a new display panel, backed by a holder - * - * @param lab - * The label to attach to this field - * @param valueHolder - * The holder to get the value from - * @param nDelay - * The delay in ms between value updates - */ - public HolderOutputPanel(final String lab, final IHolder valueHolder, final int nDelay) { - this.val = valueHolder; - this.nDelay = nDelay; - - setLayout(new HLayout(2)); - - final JLabel label = new JLabel(lab); - value = new JLabel("(stopped)"); - - updater = new Timer(nDelay, (event) -> { - value.setText(valueHolder.getValue()); - }); - - add(label); - add(value); - } - - /** - * Set this panel back to its initial state - */ - public void reset() { - stopUpdating(); - - value.setText("(stopped)"); - - updater = new Timer(nDelay, (event) -> { - value.setText(val.getValue()); - }); - } - - /** - * Start updating the contents of the field from the holder - */ - public void startUpdating() { - updater.start(); - } - - /** - * Stop updating the contents of the field from the holder - */ - public void stopUpdating() { - updater.stop(); - - value.setText(value.getText() + " (stopped)"); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/ListParameterPanel.java b/BJC-Utils2/src/main/java/bjc/utils/gui/panels/ListParameterPanel.java deleted file mode 100644 index cca73d5..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/ListParameterPanel.java +++ /dev/null @@ -1,133 +0,0 @@ -package bjc.utils.gui.panels; - -import java.util.function.Consumer; -import java.util.function.Supplier; - -import javax.swing.DefaultListModel; -import javax.swing.JButton; -import javax.swing.JList; -import javax.swing.JPanel; -import javax.swing.ListSelectionModel; - -import bjc.utils.funcdata.IList; -import bjc.utils.gui.SimpleJList; -import bjc.utils.gui.layout.HLayout; -import bjc.utils.gui.layout.VLayout; - -/** - * A panel that has a list of objects and ways of manipulating that list - * - * @author ben - * - * @param - * The type of data stored in the list - */ -public class ListParameterPanel extends JPanel { - // Version id for serialization - private static final long serialVersionUID = 3442971104975491571L; - - /** - * Create a new panel using the specified actions for doing things - * - * @param add - * The action that provides items - * @param edit - * The action that edits items - * @param remove - * The action that removes items - */ - public ListParameterPanel(final Supplier add, final Consumer edit, final Consumer remove) { - this(add, edit, remove, null); - } - - /** - * Create a new panel using the specified actions for doing things - * - * @param add - * The action that provides items - * @param edit - * The action that edits items - * @param remove - * The action that removes items - * @param defaults - * The default values to put in the list - */ - public ListParameterPanel(final Supplier add, final Consumer edit, final Consumer remove, - final IList defaults) { - setLayout(new VLayout(2)); - - JList list; - - if (defaults != null) { - list = SimpleJList.buildFromList(defaults.toIterable()); - } else { - list = new JList<>(new DefaultListModel<>()); - } - - list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - final JPanel buttonPanel = new JPanel(); - - int numButtons = 0; - - if (add != null) { - numButtons++; - } - - if (edit != null) { - numButtons++; - } - - if (remove != null) { - numButtons++; - } - - buttonPanel.setLayout(new HLayout(numButtons)); - - JButton addParam = null; - - if (add != null) { - addParam = new JButton("Add..."); - addParam.addActionListener((event) -> { - final DefaultListModel model = (DefaultListModel) list.getModel(); - - model.addElement(add.get()); - }); - } - - JButton editParam = null; - - if (edit != null) { - editParam = new JButton("Edit..."); - editParam.addActionListener((event) -> { - edit.accept(list.getSelectedValue()); - }); - } - - JButton removeParam = null; - - if (remove != null) { - removeParam = new JButton("Remove..."); - removeParam.addActionListener((event) -> { - final DefaultListModel model = (DefaultListModel) list.getModel(); - - remove.accept(model.remove(list.getSelectedIndex())); - }); - } - - if (add != null) { - buttonPanel.add(addParam); - } - - if (edit != null) { - buttonPanel.add(editParam); - } - - if (remove != null) { - buttonPanel.add(removeParam); - } - - add(list); - add(buttonPanel); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleInputPanel.java b/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleInputPanel.java deleted file mode 100644 index 65c533d..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleInputPanel.java +++ /dev/null @@ -1,45 +0,0 @@ -package bjc.utils.gui.panels; - -import java.awt.BorderLayout; - -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTextField; - -/** - * A simple component for text input - * - * @author ben - * - */ -public class SimpleInputPanel extends JPanel { - private static final long serialVersionUID = -4734279623645236868L; - - /** - * The text field containing the input value - */ - public final JTextField inputValue; - - /** - * Create a new input panel - * - * @param label - * The label for the field - * @param columns - * The number of columns of text input to take - */ - public SimpleInputPanel(final String label, final int columns) { - setLayout(new BorderLayout()); - - final JLabel inputLabel = new JLabel(label); - - if (columns < 1) { - inputValue = new JTextField(); - } else { - inputValue = new JTextField(columns); - } - - add(inputLabel, BorderLayout.LINE_START); - add(inputValue, BorderLayout.CENTER); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleListPanel.java b/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleListPanel.java deleted file mode 100644 index edc1797..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleListPanel.java +++ /dev/null @@ -1,93 +0,0 @@ -package bjc.utils.gui.panels; - -import java.awt.BorderLayout; -import java.util.function.Consumer; -import java.util.function.Predicate; - -import javax.swing.DefaultListModel; -import javax.swing.JButton; -import javax.swing.JList; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.ListSelectionModel; - -import bjc.utils.gui.layout.AutosizeLayout; -import bjc.utils.gui.layout.HLayout; - -/** - * A simple list of strings - * - * @author ben - * - */ -public class SimpleListPanel extends JPanel { - private static final long serialVersionUID = 2719963952350133541L; - - private static void addItem(final DefaultListModel model, final Predicate verifier, - final Consumer onFailure, final JTextField addItemField) { - final String potentialItem = addItemField.getText(); - - if (verifier == null || verifier.test(potentialItem)) { - model.addElement(potentialItem); - } else { - onFailure.accept(potentialItem); - } - - addItemField.setText(""); - } - - /** - * Create a new list panel - * - * @param type - * The type of things in the list - * @param model - * The model to put items into - * @param verifier - * The predicate to use to verify items - * @param onFailure - * The function to call when an item doesn't verify - */ - public SimpleListPanel(final String type, final DefaultListModel model, - final Predicate verifier, final Consumer onFailure) { - setLayout(new AutosizeLayout()); - - final JPanel itemInputPanel = new JPanel(); - itemInputPanel.setLayout(new BorderLayout()); - - final JPanel addItemPanel = new JPanel(); - addItemPanel.setLayout(new HLayout(2)); - - final JTextField addItemField = new JTextField(255); - final JButton addItemButton = new JButton("Add " + type); - - addItemPanel.add(addItemField); - addItemPanel.add(addItemButton); - - final JList itemList = new JList<>(model); - itemList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - final JScrollPane listScroller = new JScrollPane(itemList); - - final JButton removeItemButton = new JButton("Remove " + type); - - addItemButton.addActionListener((ev) -> { - addItem(model, verifier, onFailure, addItemField); - }); - - addItemField.addActionListener((ev) -> { - addItem(model, verifier, onFailure, addItemField); - }); - - removeItemButton.addActionListener((ev) -> { - model.remove(itemList.getSelectedIndex()); - }); - - itemInputPanel.add(addItemPanel, BorderLayout.PAGE_START); - itemInputPanel.add(listScroller, BorderLayout.CENTER); - itemInputPanel.add(removeItemButton, BorderLayout.PAGE_END); - - add(itemInputPanel); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleSpinnerPanel.java b/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleSpinnerPanel.java deleted file mode 100644 index 6106182..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SimpleSpinnerPanel.java +++ /dev/null @@ -1,42 +0,0 @@ -package bjc.utils.gui.panels; - -import java.awt.BorderLayout; - -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.SpinnerModel; - -/** - * A simple spinner control - * - * @author ben - * - */ -public class SimpleSpinnerPanel extends JPanel { - private static final long serialVersionUID = -4734279623645236868L; - - /** - * The spinner being used - */ - public final JSpinner inputValue; - - /** - * Create a new spinner panel - * - * @param label - * The label for the spinner - * @param model - * The model to attach to the spinner - */ - public SimpleSpinnerPanel(final String label, final SpinnerModel model) { - setLayout(new BorderLayout()); - - final JLabel inputLabel = new JLabel(label); - - inputValue = new JSpinner(model); - - add(inputLabel, BorderLayout.LINE_START); - add(inputValue, BorderLayout.CENTER); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SliderInputPanel.java b/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SliderInputPanel.java deleted file mode 100644 index e6a6da4..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/SliderInputPanel.java +++ /dev/null @@ -1,187 +0,0 @@ -package bjc.utils.gui.panels; - -import java.text.ParseException; -import java.util.function.Consumer; - -import javax.swing.JFormattedTextField; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSlider; - -import bjc.utils.gui.layout.HLayout; - -/** - * A simple input panel for a slider-controlled value and a manual-input field - * for setting the slider - * - * @author ben - * - */ -public class SliderInputPanel extends JPanel { - private final class NumberFormatter extends JFormattedTextField.AbstractFormatter { - private static final long serialVersionUID = -4448291795913908270L; - - private final int minValue; - private final int maxValue; - - private final int initValue; - - public NumberFormatter(final SliderSettings settings) { - minValue = settings.minValue; - maxValue = settings.maxValue; - - initValue = settings.initValue; - } - - @Override - public Object stringToValue(final String text) throws ParseException { - try { - final int val = Integer.parseInt(text); - - if (val < minValue) - throw new ParseException("Value must be greater than " + minValue, 0); - else if (val > maxValue) - throw new ParseException("Value must be smaller than " + maxValue, 0); - else return val; - } catch (final NumberFormatException nfex) { - final ParseException pex = new ParseException("Value must be a valid integer", 0); - - pex.initCause(nfex); - - throw pex; - } - } - - @Override - public String valueToString(final Object value) throws ParseException { - if (value == null) return Integer.toString(initValue); - - return Integer.toString((Integer) value); - } - } - - /** - * Represents the settings for a slider - * - * @author ben - * - */ - public static class SliderSettings { - /** - * The minimum value of the slider - */ - public final int minValue; - /** - * The maximum value of the slider - */ - public final int maxValue; - - /** - * The initial value of the slider - */ - public final int initValue; - - /** - * Create a new slider settings, with the initial value in the - * middle - * - * @param min - * The minimum value of the slider - * @param max - * The maximum value of the slider - */ - public SliderSettings(final int min, final int max) { - this(min, max, (min + max) / 2); - } - - /** - * Create a new set of slider sttings - * - * @param min - * The minimum slider value - * @param max - * The maximum slider value - * @param init - * Th initial slider value - */ - public SliderSettings(final int min, final int max, final int init) { - minValue = min; - maxValue = max; - - initValue = init; - } - } - - private static final long serialVersionUID = 2956394160569961404L; - private final JSlider slider; - private final JFormattedTextField field; - - /** - * Create a new slider input panel - * - * @param lab - * The label for the field - * @param settings - * The settings for slider values - * @param majorTick - * The setting for where to place big ticks - * @param minorTick - * The setting for where to place small ticks - * @param action - * The action to execute for a given value - */ - public SliderInputPanel(final String lab, final SliderSettings settings, final int majorTick, - final int minorTick, final Consumer action) { - setLayout(new HLayout(3)); - - final JLabel label = new JLabel(lab); - - slider = new JSlider(settings.minValue, settings.maxValue, settings.initValue); - field = new JFormattedTextField(new NumberFormatter(settings)); - - slider.setMajorTickSpacing(majorTick); - slider.setMinorTickSpacing(minorTick); - slider.setPaintTicks(true); - slider.setPaintLabels(true); - - slider.addChangeListener((event) -> { - if (slider.getValueIsAdjusting()) { - // Do nothing - } else { - final int val = slider.getValue(); - - field.setValue(val); - - action.accept(val); - } - }); - - field.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT); - field.setColumns(15); - field.addPropertyChangeListener("value", (event) -> { - final Object value = field.getValue(); - - if (value == null) { - // Do nothing - } else { - slider.setValue((Integer) value); - } - }); - - add(label); - add(slider); - add(field); - } - - /** - * Reset the values in this panel to a specified value - * - * @param value - * The value to reset the fields to - */ - public void resetValues(final int value) { - slider.setValue(value); - - field.setValue(value); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/package-info.java b/BJC-Utils2/src/main/java/bjc/utils/gui/panels/package-info.java deleted file mode 100644 index 4361885..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/panels/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @author ben - * - */ -package bjc.utils.gui.panels; \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/CLFormatter.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/CLFormatter.java deleted file mode 100644 index eefd532..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/CLFormatter.java +++ /dev/null @@ -1,531 +0,0 @@ -package bjc.utils.ioutils; - -import java.util.HashMap; -import java.util.IllegalFormatConversionException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UnknownFormatConversionException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import bjc.utils.PropertyDB; -import bjc.utils.esodata.Tape; -import bjc.utils.esodata.SingleTape; - -import static bjc.utils.PropertyDB.applyFormat; -import static bjc.utils.PropertyDB.getCompiledRegex; -import static bjc.utils.PropertyDB.getRegex; - -public class CLFormatter { - public static class CLModifiers { - public final boolean atMod; - public final boolean colonMod; - - public CLModifiers(boolean at, boolean colon) { - atMod = at; - colonMod = colon; - } - - public static CLModifiers fromString(String modString) { - boolean atMod = false; - boolean colonMod = false; - if(modString != null) { - atMod = modString.contains("@"); - colonMod = modString.contains(":"); - } - - return new CLModifiers(atMod, colonMod); - } - } - - public static class EscapeException extends RuntimeException { - public final boolean endIteration; - - public EscapeException() { - endIteration = false; - } - - public EscapeException(boolean end) { - endIteration = end; - } - } - - @FunctionalInterface - public interface Directive { - /* - * @TODO fill in parameters - */ - public void format(); - } - - private static final String prefixParam = getRegex("clFormatPrefix"); - private static final Pattern pPrefixParam = Pattern.compile(prefixParam); - - private static final String formatMod = getRegex("clFormatModifier"); - - private static final String prefixList = applyFormat("delimSeparatedList", prefixParam, ","); - - private static final String directiveName = getRegex("clFormatName"); - - private static final String formatDirective = applyFormat("clFormatDirective", prefixList, formatMod, directiveName); - private static final Pattern pFormatDirective = Pattern.compile(formatDirective); - - private Map extraDirectives; - - public CLFormatter() { - extraDirectives = new HashMap<>(); - } - - private void checkItem(Object itm, char directive) { - if(itm == null) - throw new IllegalArgumentException(String.format("No argument provided for %c directive", directive)); - } - - public String formatString(String format, Object... params) { - StringBuffer sb = new StringBuffer(); - /* Put the parameters where we can easily handle them. */ - Tape tParams = new SingleTape(params); - - doFormatString(format, sb, tParams); - - return sb.toString(); - } - - private void doFormatString(String format, StringBuffer sb, Tape tParams) { - Matcher dirMatcher = pFormatDirective.matcher(format); - - while(dirMatcher.find()) { - dirMatcher.appendReplacement(sb, ""); - - String dirName = dirMatcher.group("name"); - String dirFunc = dirMatcher.group("funcname"); - String dirMods = dirMatcher.group("modifiers"); - String dirParams = dirMatcher.group("params"); - - CLParameters arrParams = CLParameters.fromDirective(dirParams.split("(?": - /* @TODO - * Figure out how to implement - * tabulation/justification in a - * reasonable manner. - */ - throw new IllegalArgumentException("Layout-control directives aren't implemented yet."); - case "F": - case "E": - case "G": - case "$": - /* @TODO implement floating point directives. */ - throw new IllegalArgumentException("Floating-point directives aren't implemented yet."); - case "S": - case "W": - /* @TODO - * figure out if we want to implement - * someting for these directives instead - * of punting. - * */ - throw new IllegalArgumentException("S and W aren't implemented. Use A instead"); - default: - String msg = String.format("Unknown format directive '%s'", dirName); - throw new UnknownFormatConversionException(msg); - } - } - - dirMatcher.appendTail(sb); - } - - private void handleCDirective(StringBuffer buff, Object parm, CLModifiers mods) { - if(!(parm instanceof Character)) { - throw new IllegalFormatConversionException('C', parm.getClass()); - } - - char ch = (Character) parm; - int codepoint = (int) ch; - - if(mods.colonMod) { - /* - * Colon mod means print Unicode character name. - */ - buff.append(Character.getName(codepoint)); - } else { - buff.append(ch); - } - } - - private void handleFreshlineDirective(StringBuffer buff, CLParameters params) { - int nTimes = 1; - - if(params.length() > 1) { - nTimes = params.getInt(0, "occurance count", '&'); - } - - if(buff.charAt(buff.length() - 1) == '\n') nTimes -= 1; - - for(int i = 0; i < nTimes; i++) { - buff.append("\n"); - } - } - - private void handleLiteralDirective(StringBuffer buff, CLParameters params, String lit, char directive) { - int nTimes = 1; - - if(params.length() > 1) { - nTimes = params.getInt(0, "occurance count", directive); - } - - for(int i = 0; i < nTimes; i++) { - buff.append(lit); - } - } - - private void handleNumberDirective(StringBuffer buff, CLModifiers mods, CLParameters params, int argidx, long val, int radix) { - /* - * Initialize the two padding related parameters, and - * then fill them in from the directive parameters if - * they are present. - */ - int mincol = 0; - char padchar = ' '; - if(params.length() > (argidx + 2)) { - mincol = params.getIntDefault(argidx + 1, "minimum column count", 'R', 0); - } - if(params.length() > (argidx + 3)) { - padchar = params.getCharDefault(argidx + 2, "padding character", 'R', ' '); - } - - if(mods.colonMod) { - /* - * We're doing commas, so check if the two - * comma-related parameters were supplied. - */ - int commaInterval = 0; - char commaChar = ','; - if(params.length() > (argidx + 3)) { - commaChar = params.getCharDefault((argidx + 3), "comma character", 'R', ' '); - } - if(params.length() > (argidx + 4)) { - commaInterval = params.getIntDefault((argidx + 4), "comma interval", 'R', 0); - } - - NumberUtils.toCommaString(val, mincol, padchar, commaInterval, commaChar, mods.atMod, radix); - } else { - NumberUtils.toNormalString(val, mincol, padchar, mods.atMod, radix); - } - } - - private void handleRadixDirective(StringBuffer buff, CLModifiers mods, CLParameters params, Object arg) { - if(!(arg instanceof Number)) { - throw new IllegalFormatConversionException('R', arg.getClass()); - } - - /* - * @TODO see if this is the way we want to do this. - */ - long val = ((Number)arg).longValue(); - - if(params.length() == 0) { - if(mods.atMod) { - buff.append(NumberUtils.toRoman((Long)val, mods.colonMod)); - } else if(mods.colonMod) { - buff.append(NumberUtils.toOrdinal(val)); - } else { - buff.append(NumberUtils.toCardinal(val)); - } - } else { - if(params.length() < 1) - throw new IllegalArgumentException("R directive requires at least one parameter, the radix"); - - int radix = params.getInt(0, "radix", 'R'); - - handleNumberDirective(buff, mods, params, 0, val, radix); - } - } - - private void handleAestheticDirective(StringBuffer buff, Object item, CLModifiers mods, CLParameters params) { - int mincol = 0, colinc = 1, minpad = 0; - char padchar = ' '; - - if(params.length() > 1) { - mincol = params.getIntDefault(0, "minimum column count", 'A', 0); - } - - if(params.length() < 4) { - throw new IllegalArgumentException("Must provide either zero, one or four arguments to A directive"); - } - - colinc = params.getIntDefault(1, "padding increment", 'A', 1); - minpad = params.getIntDefault(2, "minimum amount of padding", 'A', 0); - padchar = params.getCharDefault(3, "padding character", 'A', ' '); - - StringBuilder work = new StringBuilder(); - - if(mods.atMod) { - for(int i = 0; i < minpad; i++) { - work.append(padchar); - } - - for(int i = work.length(); i < mincol; i++) { - for(int k = 0; k < colinc; k++) { - work.append(padchar); - } - } - } - - work.append(item.toString()); - - if(!mods.atMod) { - for(int i = 0; i < minpad; i++) { - work.append(padchar); - } - - for(int i = work.length(); i < mincol; i++) { - for(int k = 0; k < colinc; k++) { - work.append(padchar); - } - } - } - } - - private void handleGotoDirective(CLModifiers mods, CLParameters params, Tape formatParams) { - if(mods.colonMod) { - int num = 1; - if(params.length() > 1) { - num = params.getIntDefault(0, "number of arguments backward", '*', 1); - } - - formatParams.left(num); - } else if(mods.atMod) { - int num = 0; - if(params.length() > 1) { - num = params.getIntDefault(0, "argument index", '*', 0); - } - - formatParams.first(); - formatParams.right(num); - } else { - int num = 1; - if(params.length() > 1) { - num = params.getIntDefault(0, "number of arguments forward", '*', 1); - } - - formatParams.right(num); - } - } - - private void handleConditionalDirective(StringBuffer sb, CLModifiers mods, CLParameters arrParams, Tape formatParams, Matcher dirMatcher) { - StringBuffer condBody = new StringBuffer(); - - List clauses = new ArrayList<>(); - String defClause = null; - boolean isDefault = false; - - while(dirMatcher.find()) { - /* Process a list of clauses. */ - String dirName = dirMatcher.group("name"); - String dirMods = dirMatcher.group("modifiers"); - - if(dirName != null) { - /* Append everything up to this directive. */ - dirMatcher.appendReplacement(condBody, ""); - - if(dirName.equals("]")) { - /* End the conditional. */ - String clause = condBody.toString(); - if(isDefault) { - defClause = clause; - } else { - clauses.add(clause); - } - - break; - } else if(dirName.equals(";")) { - /* End the clause. */ - String clause = condBody.toString(); - if(isDefault) { - defClause = clause; - } else { - clauses.add(clause); - } - - /* Mark the next clause as the default. */ - if(dirMods.contains(":")) { - isDefault = true; - } - } else { - /* Not a special directive. */ - condBody.append(dirMatcher.group()); - } - } - } - - Object par = formatParams.item(); - if(mods.colonMod) { - formatParams.right(); - - if(par == null) { - throw new IllegalArgumentException("No parameter provided for [ directive."); - } else if(!(par instanceof Boolean)) { - throw new IllegalFormatConversionException('[', par.getClass()); - } - boolean res = (Boolean)par; - - String fmt; - if(res) fmt = clauses.get(1); - else fmt = clauses.get(0); - - doFormatString(fmt, sb, formatParams); - } else if(mods.atMod) { - if(par == null) { - throw new IllegalArgumentException("No parameter provided for [ directive."); - } else if(!(par instanceof Boolean)) { - throw new IllegalFormatConversionException('[', par.getClass()); - } - boolean res = (Boolean)par; - - if(res) { - doFormatString(clauses.get(0), sb, formatParams); - } else { - formatParams.right(); - } - } else { - int res; - if(arrParams.length() > 1) { - res = arrParams.getInt(0, "conditional choice", '['); - } else { - if(par == null) { - throw new IllegalArgumentException("No parameter provided for [ directive."); - } else if(!(par instanceof Number)) { - throw new IllegalFormatConversionException('[', par.getClass()); - } - res = ((Number)par).intValue(); - - formatParams.right(); - } - - if(res < 0 || res > clauses.size()) { - if(defClause != null) doFormatString(defClause, sb, formatParams); - } else { - doFormatString(clauses.get(res), sb, formatParams); - } - } - return; - } - - private void handleEscapeDirective(CLModifiers mods, CLParameters params, Tape formatParams) { - boolean shouldExit; - - switch(params.length()) { - case 0: - shouldExit = formatParams.size() == 0; - break; - case 1: - int num = params.getInt(0, "condition count", '^'); - shouldExit = num == 0; - break; - case 2: - int left = params.getInt(0, "left-hand condition", '^'); - int right = params.getInt(1, "right-hand condition", '^'); - shouldExit = left == right; - break; - case 3: - default: - int low = params.getInt(0, "lower-bound condition", '^'); - int mid = params.getInt(1, "interval condition", '^'); - int high = params.getInt(2, "upper-bound condition", '^'); - shouldExit = (low <= mid) && (mid <= high); - break; - } - - /* At negates it. */ - if(mods.atMod) shouldExit = !shouldExit; - - if(shouldExit) throw new EscapeException(mods.colonMod); - } - - -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/CLParameters.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/CLParameters.java deleted file mode 100644 index e4bb6fb..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/CLParameters.java +++ /dev/null @@ -1,109 +0,0 @@ -package bjc.utils.ioutils; - -import java.util.ArrayList; -import java.util.List; - -import bjc.utils.esodata.Tape; - -/** - * Represents a set of parameters to a CL format directive. - * - * @author Benjamin Culkin - */ -public class CLParameters { - private String[] params; - - public CLParameters(String[] params) { - this.params = params; - } - - public int length() { - return params.length; - } - - /** - * Creates a set of parameters from an array of parameters. - * - * Mostly, this just fills in V and # parameters. - * - * @param params - * The parameters of the directive. - * @param dirParams - * The parameters of the format string. - * - * @return A set of CL parameters. - */ - public static CLParameters fromDirective(String[] params, Tape dirParams) { - List parameters = new ArrayList<>(); - - for(String param : params) { - if(param.equalsIgnoreCase("V")) { - Object par = dirParams.item(); - boolean succ = dirParams.right(); - - if(par == null) { - throw new IllegalArgumentException("Expected a format parameter for V inline parameter"); - } - - if(par instanceof Number) { - int val = ((Number)par).intValue(); - - parameters.add(Integer.toString(val)); - } else if(par instanceof Character) { - char ch = ((Character)par); - - parameters.add(Character.toString(ch)); - } else { - throw new IllegalArgumentException("Incorrect type of parameter for V inline parameter"); - } - } else if(param.equals("#")) { - parameters.add(Integer.toString(dirParams.position())); - } else { - parameters.add(param); - } - } - - return new CLParameters(parameters.toArray(new String[0])); - } - - public char getCharDefault(int idx, String paramName, char directive, char def) { - if(!params[idx].equals("")) { - return getChar(idx, paramName, directive); - } - - return def; - } - - public char getChar(int idx, String paramName, char directive) { - String param = params[idx]; - - if(!param.startsWith("'")) { - throw new IllegalArgumentException(String.format("Invalid %s %s to %c directive", paramName, param, directive)); - } - - return param.charAt(1); - } - - public int getIntDefault(int idx, String paramName, char directive, int def) { - if(!params[idx].equals("")) { - - } - - return def; - } - - public int getInt(int idx, String paramName, char directive) { - String param = params[idx]; - - try { - return Integer.parseInt(param); - } catch (NumberFormatException nfex) { - String msg = String.format("Invalid %s %s to %c directive", paramName, param, directive); - - IllegalArgumentException iaex = new IllegalArgumentException(msg); - iaex.initCause(nfex); - - throw iaex; - } - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/NumberUtils.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/NumberUtils.java deleted file mode 100644 index 1b754e2..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/NumberUtils.java +++ /dev/null @@ -1,405 +0,0 @@ -package bjc.utils.ioutils; - -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.LongPredicate; - -import static java.util.Map.Entry; - -public class NumberUtils { - /* - * @TODO Use U+305 for large roman numerals, as well as excels 'concise' - * numerals (as implemented by roman()). - */ - public static String toRoman(long number, boolean classic) { - StringBuilder work = new StringBuilder(); - - long currNumber = number; - - if(currNumber == 0) { - return "N"; - } - - if(currNumber < 0) { - currNumber *= -1; - - work.append("-"); - } - - if(currNumber >= 1000) { - int numM = (int)(currNumber / 1000); - currNumber = currNumber % 1000; - - for(int i = 0; i < numM; i++) { - work.append("M"); - } - } - - if(currNumber >= 900 && !classic) { - currNumber = currNumber % 900; - - work.append("CM"); - } - - if(currNumber >= 500) { - currNumber = currNumber % 500; - - work.append("D"); - } - - if(currNumber >= 400 && !classic) { - currNumber = currNumber % 400; - - work.append("CD"); - } - - if(currNumber >= 100) { - int numC = (int)(currNumber / 100); - currNumber = currNumber % 100; - - for(int i = 0; i < numC; i++) { - work.append("C"); - } - } - - if(currNumber >= 90 && !classic) { - currNumber = currNumber % 90; - - work.append("XC"); - } - - if(currNumber >= 50) { - currNumber = currNumber % 50; - - work.append("L"); - } - - if(currNumber >= 40 && !classic) { - currNumber = currNumber % 40; - - work.append("XL"); - } - - if(currNumber >= 10) { - int numX = (int)(currNumber / 10); - currNumber = currNumber % 10; - - for(int i = 0; i < numX; i++) { - work.append("X"); - } - } - - if(currNumber >= 9 && !classic) { - currNumber = currNumber % 9; - - work.append("IX"); - } - - if(currNumber >= 5) { - currNumber = currNumber % 5; - - work.append("V"); - } - - if(currNumber >= 4 && !classic) { - currNumber = currNumber % 4; - - work.append("IV"); - } - - if(currNumber >= 1) { - int numI = (int)(currNumber / 1); - currNumber = currNumber % 1; - - for(int i = 0; i < numI; i++) { - work.append("I"); - } - } - - return work.toString(); - } - - public static String toCardinal(long number) { - return toCardinal(number, null); - } - - private static String[] cardinals = new String[] { - "zero", "one", "two", "three", "four", "five", "six", "seven", - "eight", "nine", "ten", "eleven", "twelve", "thirteen", - "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", - "nineteen", "twenty", - }; - - public static class CardinalState { - public final Map customNumbers; - public final Map> customScales; - - public CardinalState(Map customNumbers, Map> customScales) { - this.customNumbers = customNumbers; - this.customScales = customScales; - } - - public String handleCustom(long number) { - if(customNumbers.containsKey(number)) { - return customNumbers.get(number); - } - - for(Entry> ent : customScales.entrySet()) { - if(ent.getKey().test(number)) { - return ent.getValue().apply(number, this); - } - } - - return null; - } - } - - public static String toCardinal(long number, CardinalState custom) { - if(custom != null) { - String res = custom.handleCustom(number); - - if(res != null) return res; - } - - if(number < 0) return "negative " + toCardinal(number * -1, custom); - - if(number <= 20) return cardinals[(int)number]; - - if(number < 100) { - if(number % 10 == 0) { - switch((int)number) { - case 30: - return "thirty"; - case 40: - return "forty"; - case 50: - return "fifty"; - case 60: - return "sixty"; - case 70: - return "seventy"; - case 80: - return "eighty"; - case 90: - return "ninety"; - default: - /* - * Shouldn't happen. - */ - assert(false); - } - } - - long numTens = (long)(number / 10); - long numOnes = number % 10; - - return toCardinal(numTens, custom) + "-" + toCardinal(numOnes, custom); - } - - if(number < 1000) { - long numHundreds = (long)(number / 100); - long rest = number % 100; - - return toCardinal(numHundreds, custom) + " hundred and " + toCardinal(rest, custom); - } - - long MILLION = (long)(Math.pow(10, 6)); - if(number < MILLION) { - long numThousands = (long)(number / 1000); - long rest = number % 1000; - - return toCardinal(numThousands, custom) + " thousand, " + toCardinal(rest, custom); - } - - long BILLION = (long)(Math.pow(10, 9)); - if(number < BILLION) { - long numMillions = (long)(number / MILLION); - long rest = number % MILLION; - - return toCardinal(numMillions, custom) + " million, " + toCardinal(rest, custom); - } - - long TRILLION = (long)(Math.pow(10, 12)); - if(number < TRILLION) { - long numBillions = (long)(number / BILLION); - long rest = number % BILLION; - - return toCardinal(numBillions, custom) + " billion, " + toCardinal(rest, custom); - } - - throw new IllegalArgumentException("Numbers greater than or equal to 1 trillion are not supported yet."); - } - - public static String toOrdinal(long number) { - if(number < 0) { - return "minus " + toOrdinal(number); - } - - if(number < 20) { - switch((int)number) { - case 0: - return "zeroth"; - case 1: - return "first"; - case 2: - return "second"; - case 3: - return "third"; - case 4: - return "fourth"; - case 5: - return "fifth"; - case 6: - return "sixth"; - case 7: - return "seventh"; - case 8: - return "eighth"; - case 9: - return "ninth"; - case 10: - return "tenth"; - case 11: - return "eleventh"; - case 12: - return "twelfth"; - case 13: - return "thirteenth"; - case 14: - return "fourteenth"; - case 15: - return "fifteenth"; - case 16: - return "sixteenth"; - case 17: - return "seventeenth"; - case 18: - return "eighteenth"; - case 19: - return "nineteenth"; - default: - /* - * Shouldn't happen. - */ - assert(false); - } - } - - if(number < 100) { - if(number % 10 == 0) { - switch((int)number) { - case 20: - return "twentieth"; - case 30: - return "thirtieth"; - case 40: - return "fortieth"; - case 50: - return "fiftieth"; - case 60: - return "sixtieth"; - case 70: - return "seventieth"; - case 80: - return "eightieth"; - case 90: - return "ninetieth"; - } - } - - long numPostfix = number % 10; - return toCardinal(number - numPostfix) + "-" + toOrdinal(numPostfix); - } - - long procNum = number % 100; - long tens = (long)(procNum / 10); - long ones = procNum % 10; - - if(tens == 1) { - return Long.toString(number) + "th"; - } - - switch((int)ones) { - case 1: - return Long.toString(number) + "st"; - case 2: - return Long.toString(number) + "nd"; - case 3: - return Long.toString(number) + "rd"; - default: - return Long.toString(number) + "th"; - } - } - - private static char[] radixChars = new char[62]; - static { - int idx = 0; - - for(char i = 0; i < 10; i++) { - radixChars[idx] = (char)('0' + i); - - idx += 1; - } - - for(char i = 0; i < 26; i++) { - radixChars[idx] = (char)('A' + i); - - idx += 1; - } - - for(char i = 0; i < 26; i++) { - radixChars[idx] = (char)('a' + i); - - idx += 1; - } - } - - public static String toCommaString(long val, int mincols, char padchar, int commaInterval, char commaChar, boolean signed, int radix) { - if(radix > radixChars.length) { - throw new IllegalArgumentException(String.format("Radix %d is larger than largest supported radix %d", radix, radixChars.length)); - } - - StringBuilder work = new StringBuilder(); - - boolean isNeg = false; - long currVal = val; - if(currVal < 0) { - isNeg = true; - currVal *= -1; - } - - if(currVal == 0) { - work.append(radixChars[0]); - } else { - int valCounter = 0; - - while(currVal != 0) { - valCounter += 1; - - int radDigit = (int)(currVal % radix); - work.append(radixChars[radDigit]); - currVal = (long)(currVal / radix); - - if(commaInterval != 0 && valCounter % commaInterval == 0) work.append(commaChar); - } - } - - if(isNeg) work.append("-"); - else if(signed) work.append("+"); - - work.reverse(); - - /* @TODO Should we have some way to specify how to pad? */ - if(work.length() < mincols) { - for(int i = work.length(); i < mincols; i++) { - work.append(padchar); - } - } - - return work.toString(); - } - - public static String toNormalString(long val, int mincols, char padchar, boolean signed, int radix) { - return toCommaString(val, mincols, padchar, 0, ',', signed, radix); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/Prompter.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/Prompter.java deleted file mode 100644 index a6ec4c0..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/Prompter.java +++ /dev/null @@ -1,47 +0,0 @@ -package bjc.utils.ioutils; - -import java.io.PrintStream; - -import bjc.utils.ioutils.blocks.TriggeredBlockReader; - -/** - * A runnable for use with {@link TriggeredBlockReader} to prompt the user for - * input. - * - * @author bjculkin - * - */ -public final class Prompter implements Runnable { - private String promt; - private final PrintStream printer; - - /** - * Create a new prompter using the specified prompt. - * - * @param prompt - * The prompt to present. - * - * @param output - * The stream to print the prompt on. - */ - public Prompter(final String prompt, final PrintStream output) { - promt = prompt; - - printer = output; - } - - /** - * Set the prompt this prompter uses. - * - * @param prompt - * The prompt this prompter uses. - */ - public void setPrompt(final String prompt) { - promt = prompt; - } - - @Override - public void run() { - printer.print(promt); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RegexStringEditor.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/RegexStringEditor.java deleted file mode 100644 index 71f6782..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RegexStringEditor.java +++ /dev/null @@ -1,230 +0,0 @@ -package bjc.utils.ioutils; - -import java.util.function.BiFunction; -import java.util.function.UnaryOperator; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import bjc.utils.data.Toggle; -import bjc.utils.data.ValueToggle; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; -import bjc.utils.functypes.ID; - -/** - * Editor methods for strings based off the command language for the Sam editor. - * - * @author EVE - * - */ -public class RegexStringEditor { - private static final UnaryOperator SID = ID.id(); - - /** - * Replace every occurrence of the pattern with the result of applying - * the action to the string matched by the pattern. - * - * @param input - * The input string to process. - * - * @param patt - * The pattern to match the string against. - * - * @param action - * The action to transform matches with. - * - * @return The string, with matches replaced with the action. - */ - public static String onOccurances(final String input, final Pattern patt, final UnaryOperator action) { - return reduceOccurances(input, patt, SID, action); - } - - /** - * Replace every occurrence between the patterns with the result of - * applying the action to the strings between the patterns. - * - * @param input - * The input string to process. - * - * @param patt - * The pattern to match the string against. - * - * @param action - * The action to transform matches with. - * - * @return The string, with strings between the matches replaced with - * the action. - */ - public static String betweenOccurances(final String input, final Pattern patt, - final UnaryOperator action) { - return reduceOccurances(input, patt, action, SID); - } - - /** - * Execute actions between and on matches of a regular expression. - * - * @param input - * The input string. - * - * @param rPatt - * The pattern to match against the string. - * - * @param betweenAction - * The function to execute between matches of the string. - * - * @param onAction - * The function to execute on matches of the string. - * - * @return The string, with both actions applied. - */ - public static String reduceOccurances(final String input, final Pattern rPatt, - final UnaryOperator betweenAction, final UnaryOperator onAction) { - /* - * Get all of the occurances. - */ - final IList occurances = listOccurances(input, rPatt); - - /* - * Execute the correct action on every occurance. - */ - final Toggle> actions = new ValueToggle<>(onAction, betweenAction); - final BiFunction reducer = (strang, state) -> { - return state.append(actions.get().apply(strang)); - }; - - /* - * Convert the list back to a string. - */ - return occurances.reduceAux(new StringBuilder(), reducer, StringBuilder::toString); - } - - /** - * Execute actions between and on matches of a regular expression. - * - * @param input - * The input string. - * - * @param rPatt - * The pattern to match against the string. - * - * @param betweenAction - * The function to execute between matches of the string. - * - * @param onAction - * The function to execute on matches of the string. - * - * @return The string, with both actions applied. - */ - public static IList mapOccurances(final String input, final Pattern rPatt, - final UnaryOperator betweenAction, final UnaryOperator onAction) { - /* - * Get all of the occurances. - */ - final IList occurances = listOccurances(input, rPatt); - - /* - * Execute the correct action on every occurance. - */ - final Toggle> actions = new ValueToggle<>(onAction, betweenAction); - return occurances.map(strang -> actions.get().apply(strang)); - } - - /** - * Separate a string into match/non-match segments. - * - * @param input - * The string to separate. - * - * @param rPatt - * The pattern to use for separation. - * - * @return The string, as a list of match/non-match segments, - * starting/ending with a non-match segment. - */ - public static IList listOccurances(final String input, final Pattern rPatt) { - final IList res = new FunctionalList<>(); - - /* - * Create the matcher and work buffer. - */ - final Matcher matcher = rPatt.matcher(input); - StringBuffer work = new StringBuffer(); - - /* - * For every match. - */ - while (matcher.find()) { - final String match = matcher.group(); - - /* - * Append the text until the match to the buffer. - */ - matcher.appendReplacement(work, ""); - - res.add(work.toString()); - res.add(match); - - /* - * Clear the buffer. - */ - work = new StringBuffer(); - } - - /* - * Add the text after the last match to the buffer. - */ - matcher.appendTail(work); - res.add(work.toString()); - - return res; - } - - /** - * Apply an operation to a string if it matches a regular expression. - * - * @param input - * The input string. - * - * @param patt - * The pattern to match against it. - * - * @param action - * The action to execute if it matches. - * - * @return The string, modified by the action if the pattern matched. - */ - public static String ifMatches(final String input, final Pattern patt, final UnaryOperator action) { - final Matcher matcher = patt.matcher(input); - - if (matcher.matches()) { - return action.apply(input); - } else { - return input; - } - } - - /** - * Apply an operation to a string if it matches a regular expression. - * - * @param input - * The input string. - * - * @param patt - * The pattern to match against it. - * - * @param action - * The action to execute if it doesn't match. - * - * @return The string, modified by the action if the pattern didn't - * match. - */ - public static String ifNotMatches(final String input, final Pattern patt, final UnaryOperator action) { - final Matcher matcher = patt.matcher(input); - - if (matcher.matches()) { - return input; - } else { - return action.apply(input); - } - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java deleted file mode 100644 index 7c5205b..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java +++ /dev/null @@ -1,265 +0,0 @@ -package bjc.utils.ioutils; - -import java.io.InputStream; -import java.util.InputMismatchException; -import java.util.Scanner; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import bjc.utils.data.IHolder; -import bjc.utils.data.IPair; -import bjc.utils.data.Identity; -import bjc.utils.data.Pair; -import bjc.utils.exceptions.UnknownPragmaException; -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.FunctionalStringTokenizer; -import bjc.utils.funcdata.IMap; - -/** - * This class parses a rules based config file, and uses it to drive a provided - * set of actions - * - * @author ben - * - * @param - * The type of the state object to use - * - */ -public class RuleBasedConfigReader { - /* Function to execute when starting a rule. - * Takes the tokenizer, and a pair of the read token and application state - */ - private BiConsumer> start; - - /* - * Function to use when continuing a rule - * Takes a tokenizer and application state - */ - private BiConsumer continueRule; - - /* - * Function to use when ending a rule - * Takes an application state - */ - private Consumer end; - - /* - * Map of pragma names to pragma actions - * Pragma actions are functions taking a tokenizer and application state - */ - private final IMap> pragmas; - - /** - * Create a new rule-based config reader - * - * @param start - * The action to fire when starting a rule - * @param continueRule - * The action to fire when continuing a rule - * @param end - * The action to fire when ending a rule - */ - public RuleBasedConfigReader(final BiConsumer> start, - final BiConsumer continueRule, final Consumer end) { - this.start = start; - this.continueRule = continueRule; - this.end = end; - - this.pragmas = new FunctionalMap<>(); - } - - /** - * Add a pragma to this reader - * - * @param name - * The name of the pragma to add - * @param action - * The function to execute when this pragma is read - */ - public void addPragma(final String name, final BiConsumer action) { - if (name == null) throw new NullPointerException("Pragma name must not be null"); - else if (action == null) throw new NullPointerException("Pragma action must not be null"); - - pragmas.put(name, action); - } - - private void continueRule(final E state, final boolean isRuleOpen, final String line) { - // Make sure our input is correct - if (isRuleOpen == false) - throw new InputMismatchException("Cannot continue rule with no rule open"); - else if (continueRule == null) - throw new InputMismatchException("Rule continuation not supported for current grammar"); - - /* - * Accept the rule - */ - continueRule.accept(new FunctionalStringTokenizer(line.substring(1), " "), state); - } - - private boolean endRule(final E state, final boolean isRuleOpen) { - /* - * Ignore blank line without an open rule - */ - if (isRuleOpen == false) - /* - * Do nothing - */ - return false; - else { - /* - * Nothing happens on rule end - */ - if (end != null) { - /* - * Process the rule ending - */ - end.accept(state); - } - - /* - * Return a closed rule - */ - return false; - } - } - - /** - * Run a stream through this reader - * - * @param input - * The stream to get input - * @param initialState - * The initial state of the reader - * @return The final state of the reader - */ - public E fromStream(final InputStream input, final E initialState) { - if (input == null) throw new NullPointerException("Input stream must not be null"); - - /* - * Application state: We're giving this back later - */ - final E state = initialState; - - /* - * Prepare our input source - */ - try (Scanner source = new Scanner(input)) { - source.useDelimiter("\n"); - /* - * This is true when a rule's open - */ - final IHolder isRuleOpen = new Identity<>(false); - - /* - * Do something for every line of the file - */ - source.forEachRemaining((line) -> { - /* - * Skip comment lines - */ - if (line.startsWith("#") || line.startsWith("//")) - /* - * It's a comment - */ - return; - else if (line.equals("")) { - /* - * End the rule - */ - isRuleOpen.replace(endRule(state, isRuleOpen.getValue())); - } else if (line.startsWith("\t")) { - /* - * Continue the rule - */ - continueRule(state, isRuleOpen.getValue(), line); - } else { - /* - * Open a rule - */ - isRuleOpen.replace(startRule(state, isRuleOpen.getValue(), line)); - } - }); - } - - /* - * Return the state that the user has created - */ - return state; - } - - /** - * Set the action to execute when continuing a rule - * - * @param continueRule - * The action to execute on continuation of a rule - */ - public void setContinueRule(final BiConsumer continueRule) { - this.continueRule = continueRule; - } - - /** - * Set the action to execute when ending a rule - * - * @param end - * The action to execute on ending of a rule - */ - public void setEndRule(final Consumer end) { - this.end = end; - } - - /** - * Set the action to execute when starting a rule - * - * @param start - * The action to execute on starting of a rule - */ - public void setStartRule(final BiConsumer> start) { - if (start == null) throw new NullPointerException("Action on rule start must be non-null"); - - this.start = start; - } - - private boolean startRule(final E state, boolean isRuleOpen, final String line) { - /* - * Create the line tokenizer - */ - final FunctionalStringTokenizer tokenizer = new FunctionalStringTokenizer(line, " "); - - /* - * Get the initial token - */ - final String nextToken = tokenizer.nextToken(); - - /* - * Handle pragmas - */ - if (nextToken.equals("pragma")) { - /* - * Get the pragma name - */ - final String token = tokenizer.nextToken(); - - /* - * Handle pragmas - */ - pragmas.getOrDefault(token, (tokenzer, stat) -> { - throw new UnknownPragmaException("Unknown pragma " + token); - }).accept(tokenizer, state); - } else { - /* - * Make sure input is correct - */ - if (isRuleOpen == true) - throw new InputMismatchException("Nested rules are currently not supported"); - - /* - * Start a rule - */ - start.accept(tokenizer, new Pair<>(nextToken, state)); - - isRuleOpen = true; - } - - return isRuleOpen; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java deleted file mode 100644 index e26a7ee..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java +++ /dev/null @@ -1,100 +0,0 @@ -package bjc.utils.ioutils; - -import java.util.function.BiConsumer; - -import bjc.utils.exceptions.PragmaFormatException; -import bjc.utils.funcdata.FunctionalStringTokenizer; -import bjc.utils.funcutils.ListUtils; - -/** - * Contains factory methods for common pragma types - * - * @author ben - * - */ -public class RuleBasedReaderPragmas { - - /** - * Creates a pragma that takes a single integer argument - * - * @param - * The type of state that goes along with this pragma - * @param name - * The name of this pragma, for error message purpose - * @param consumer - * The function to invoke with the parsed integer - * @return A pragma that functions as described above. - */ - public static BiConsumer buildInteger(final String name, - final BiConsumer consumer) { - return (tokenizer, state) -> { - /* - * Check our input is correct - */ - if (!tokenizer.hasMoreTokens()) { - String fmt = "Pragma %s requires one integer argument"; - - throw new PragmaFormatException(String.format(fmt, name)); - } - - /* - * Read the argument - */ - final String token = tokenizer.nextToken(); - - try { - /* - * Run the pragma - */ - consumer.accept(Integer.parseInt(token), state); - } catch (final NumberFormatException nfex) { - /* - * Tell the user their argument isn't correct - */ - String fmt = "Argument %s to %s pragma isn't a valid integer, and this pragma requires an integer argument."; - - final PragmaFormatException pfex = new PragmaFormatException(String.format(fmt, token, name)); - - pfex.initCause(nfex); - - throw pfex; - } - }; - } - - /** - * Creates a pragma that takes any number of arguments and collapses - * them all into a single string - * - * @param - * The type of state that goes along with this pragma - * @param name - * The name of this pragma, for error message purpose - * @param consumer - * The function to invoke with the parsed string - * @return A pragma that functions as described above. - */ - public static BiConsumer buildStringCollapser( - final String name, final BiConsumer consumer) { - return (tokenizer, state) -> { - /* - * Check our input - */ - if (!tokenizer.hasMoreTokens()) { - String fmt = "Pragma %s requires one or more string arguments."; - - throw new PragmaFormatException(String.format(fmt, name)); - } - - /* - * Build our argument - */ - final String collapsed = ListUtils.collapseTokens(tokenizer.toList()); - - /* - * Run the pragma - */ - consumer.accept(collapsed, state); - }; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/SimpleProperties.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/SimpleProperties.java deleted file mode 100644 index e6279c4..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/SimpleProperties.java +++ /dev/null @@ -1,170 +0,0 @@ -package bjc.utils.ioutils; - -import java.io.InputStream; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Scanner; -import java.util.Set; - -/** - * Simple file based properties. - * - * @author EVE - * - */ -public class SimpleProperties implements Map { - private final Map props; - - /** - * Create a new set of simple properties. - */ - public SimpleProperties() { - props = new HashMap<>(); - } - - /** - * Load properties from the provided input stream. - * - * The format is the name, a space, then the body. - * - * All leading/trailing spaces from the name & body are removed. - * - * @param is - * The stream to read from. - * - * @param allowDuplicates - * Whether or not duplicate keys should be allowed. - */ - public void loadFrom(final InputStream is, final boolean allowDuplicates) { - try (Scanner scn = new Scanner(is)) { - while (scn.hasNextLine()) { - final String ln = scn.nextLine().trim(); - - /* - * Skip blank lines/comments - */ - if (ln.equals("")) { - continue; - } - if (ln.startsWith("#")) { - continue; - } - - final int sepIdx = ln.indexOf(' '); - - /* - * Complain about improperly formatted lines. - */ - if (sepIdx == -1) { - final String fmt = "Properties must be a name, a space, then the body.\n\tOffending line is '%s'"; - final String msg = String.format(fmt, ln); - - throw new NoSuchElementException(msg); - } - - final String name = ln.substring(0, sepIdx).trim(); - final String body = ln.substring(sepIdx).trim(); - - /* - * Complain about duplicates, if that is wanted. - */ - if (!allowDuplicates && containsKey(name)) { - final String msg = String.format("Duplicate key '%s'", name); - - throw new IllegalStateException(msg); - } - - put(name, body); - } - } - } - - /** - * Output the set of read properties. - */ - public void outputProperties() { - System.out.println("Read properties:"); - - for (final Entry entry : entrySet()) { - System.out.printf("\t'%s'\t'%s'\n", entry.getKey(), entry.getValue()); - } - - System.out.println(); - } - - @Override - public int size() { - return props.size(); - } - - @Override - public boolean isEmpty() { - return props.isEmpty(); - } - - @SuppressWarnings("unlikely-arg-type") - @Override - public boolean containsKey(final Object key) { - return props.containsKey(key); - } - - @SuppressWarnings("unlikely-arg-type") - @Override - public boolean containsValue(final Object value) { - return props.containsValue(value); - } - - @SuppressWarnings("unlikely-arg-type") - @Override - public String get(final Object key) { - return props.get(key); - } - - @Override - public String put(final String key, final String value) { - return props.put(key, value); - } - - @SuppressWarnings("unlikely-arg-type") - @Override - public String remove(final Object key) { - return props.remove(key); - } - - @Override - public void putAll(final Map m) { - props.putAll(m); - } - - @Override - public void clear() { - props.clear(); - } - - @Override - public Set keySet() { - return props.keySet(); - } - - @Override - public Collection values() { - return props.values(); - } - - @Override - public Set> entrySet() { - return props.entrySet(); - } - - @Override - public boolean equals(final Object o) { - return props.equals(o); - } - - @Override - public int hashCode() { - return props.hashCode(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java deleted file mode 100644 index 15f3510..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java +++ /dev/null @@ -1,88 +0,0 @@ -package bjc.utils.ioutils.blocks; - -/** - * Represents a block of text read in from a source. - * - * @author EVE - * - */ -public class Block { - /** - * The contents of this block. - */ - public final String contents; - - /** - * The line of the source this block started on. - */ - public final int startLine; - - /** - * The line of the source this block ended on. - */ - public final int endLine; - - /** - * The number of this block. - */ - public final int blockNo; - - /** - * Create a new block. - * - * @param blockNo - * The number of this block. - * @param contents - * The contents of this block. - * @param startLine - * The line this block started on. - * @param endLine - * The line this block ended. - */ - public Block(final int blockNo, final String contents, final int startLine, final int endLine) { - this.contents = contents; - this.startLine = startLine; - this.endLine = endLine; - this.blockNo = blockNo; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + blockNo; - result = prime * result + (contents == null ? 0 : contents.hashCode()); - result = prime * result + endLine; - result = prime * result + startLine; - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof Block)) return false; - - final Block other = (Block) obj; - - if (blockNo != other.blockNo) return false; - - if (contents == null) { - if (other.contents != null) return false; - } else if (!contents.equals(other.contents)) return false; - - if (endLine != other.endLine) return false; - if (startLine != other.startLine) return false; - - return true; - } - - @Override - public String toString() { - String fmt = "Block #%d (from lines %d to %d), length: %d characters"; - - return String.format(fmt, blockNo, startLine, endLine, contents.length()); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java deleted file mode 100644 index 3c695c6..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java +++ /dev/null @@ -1,73 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.IOException; -import java.util.Iterator; -import java.util.function.Consumer; - -/** - * A source of blocks of characters, marked with line numbers as to block - * start/block end. - * - * @author bjculkin - * - */ -public interface BlockReader extends AutoCloseable, Iterator { - /** - * Check if this reader has an available block. - * - * @return Whether or not another block is available. - */ - boolean hasNextBlock(); - - /** - * Get the current block. - * - * @return The current block, or null if there is no current block. - */ - Block getBlock(); - - /** - * Move to the next block. - * - * @return Whether or not the next block was successfully read. - */ - boolean nextBlock(); - - /** - * Retrieve the number of blocks that have been read so far. - * - * @return The number of blocks read so far. - */ - int getBlockCount(); - - @Override - void close() throws IOException; - - /* - * Methods with default impls. - */ - - /** - * Execute an action for each remaining block. - * - * @param action - * The action to execute for each block - */ - default void forEachBlock(final Consumer action) { - while (hasNext()) { - action.accept(next()); - } - } - - @Override - default boolean hasNext() { - return hasNextBlock(); - } - - @Override - default Block next() { - nextBlock(); - - return getBlock(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java deleted file mode 100644 index 8bbb89c..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java +++ /dev/null @@ -1,81 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.Reader; - -/** - * Utility methods for constructing instances of {@link BlockReader} - * - * @author bjculkin - * - */ -public class BlockReaders { - /** - * Create a new simple block reader that works off a regex. - * - * @param blockDelim - * The regex that separates blocks. - * - * @param source - * The reader to get blocks from. - * - * @return A configured simple reader. - */ - public static SimpleBlockReader simple(final String blockDelim, final Reader source) { - return new SimpleBlockReader(blockDelim, source); - } - - /** - * Create a new pushback block reader. - * - * @param src - * The block reader to read blocks from. - * - * @return A configured pushback reader. - */ - public static PushbackBlockReader pushback(final BlockReader src) { - return new PushbackBlockReader(src); - } - - /** - * Create a new triggered block reader. - * - * @param source - * The block reader to read blocks from. - * - * @param action - * The action to execute before reading a block. - * - * @return A configured triggered block reader. - */ - public static BlockReader trigger(final BlockReader source, final Runnable action) { - return new TriggeredBlockReader(source, action); - } - - /** - * Create a new layered block reader. - * - * @param primary - * The first source to read blocks from. - * - * @param secondary - * The second source to read blocks from. - * - * @return A configured layered block reader. - */ - public static BlockReader layered(final BlockReader primary, final BlockReader secondary) { - return new LayeredBlockReader(primary, secondary); - } - - /** - * Create a new serial block reader. - * - * @param readers - * The readers to pull from, in the order to pull from - * them. - * - * @return A configured serial block reader. - */ - public static BlockReader serial(final BlockReader... readers) { - return new SerialBlockReader(readers); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BoundBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BoundBlockReader.java deleted file mode 100644 index b1e82d7..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BoundBlockReader.java +++ /dev/null @@ -1,61 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.IOException; - -import java.util.function.BooleanSupplier; -import java.util.function.Supplier; - -public class BoundBlockReader implements BlockReader { - @FunctionalInterface - public interface Closer { - public void close() throws IOException; - } - - private BooleanSupplier checker; - private Supplier getter; - private Closer closer; - - private Block current; - - private int blockNo; - - public BoundBlockReader(BooleanSupplier blockChecker, Supplier blockGetter, Closer blockCloser) { - checker = blockChecker; - getter = blockGetter; - closer = blockCloser; - - blockNo = 0; - } - - @Override - public boolean hasNextBlock() { - return checker.getAsBoolean(); - } - - @Override - public Block getBlock() { - return current; - } - - @Override - public boolean nextBlock() { - if(checker.getAsBoolean()) { - current = getter.get(); - blockNo += 1; - - return true; - } - - return false; - } - - @Override - public int getBlockCount() { - return blockNo; - } - - @Override - public void close() throws IOException { - closer.close(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java deleted file mode 100644 index 0b43f7a..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java +++ /dev/null @@ -1,97 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.IOException; - -import java.util.function.Consumer; -import java.util.function.Predicate; - -public class FilteredBlockReader implements BlockReader { - /* - * The source of blocks. - */ - private BlockReader source; - - /* - * The current and next block. - * - * Both have already been checked for the predicate. - */ - private Block current; - private Block pending; - - /* - * Number of blocks that passed the predicate. - */ - private int blockNo; - - /* - * The predicate blocks must pass. - */ - private Predicate pred; - - /* - * The action to call on failure, if there is one. - */ - private Consumer failAction; - - public FilteredBlockReader(BlockReader src, Predicate predic) { - this(src, predic, null); - } - - public FilteredBlockReader(BlockReader src, Predicate predic, Consumer failAct) { - source = src; - pred = predic; - failAction = failAct; - - blockNo = 0; - } - - @Override - public boolean hasNextBlock() { - if(pending != null) return true; - - while(source.hasNextBlock()) { - /* - * Only say we have a next block if the next block would - * pass the predicate. - */ - pending = source.next(); - - if(pred.test(pending)) { - blockNo += 1; - return true; - } else { - failAction.accept(pending); - } - } - - return false; - } - - @Override - public Block getBlock() { - return current; - } - - @Override - public boolean nextBlock() { - if(pending != null || hasNextBlock()) { - current = pending; - pending = null; - - return true; - } - - return false; - } - - @Override - public int getBlockCount() { - return blockNo; - } - - @Override - public void close() throws IOException { - source.close(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FlatMappedBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FlatMappedBlockReader.java deleted file mode 100644 index f4d8439..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FlatMappedBlockReader.java +++ /dev/null @@ -1,86 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.IOException; - -import java.util.Iterator; -import java.util.List; -import java.util.function.Function; -import java.util.function.UnaryOperator; - -/** - * A block reader that supports applying a flatmap operation to blocks. - * - * The use-case in mind for this was tokenizing blocks. - * - * @author Benjamin Culkin - */ -public class FlatMappedBlockReader implements BlockReader { - /* - * The source reader. - */ - private BlockReader reader; - - /* - * The current block, and any blocks pending from the last source block. - */ - private Iterator pending; - private Block current; - - /* - * The operator to open blocks with. - */ - private Function> transform; - - /* - * The current block number. - */ - private int blockNo; - - public FlatMappedBlockReader(BlockReader source, Function> trans) { - reader = source; - transform = trans; - - blockNo = 0; - } - - @Override - public boolean hasNextBlock() { - return pending.hasNext() || reader.hasNextBlock(); - } - - @Override - public Block getBlock() { - return current; - } - - @Override - public boolean nextBlock() { - /* - * Attempt to get a new pending list if the one we have isn't - * valid. - */ - while(pending == null || !pending.hasNext()) { - if(!reader.hasNext()) return false; - - pending = transform.apply(reader.next()).iterator(); - } - - /* - * Advance the iterator. - */ - current = pending.next(); - blockNo += 1; - - return true; - } - - @Override - public int getBlockCount() { - return blockNo; - } - - @Override - public void close() throws IOException { - reader.close(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java deleted file mode 100644 index 967a1f2..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java +++ /dev/null @@ -1,81 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.IOException; - -/** - * A block reader that supports draining all the blocks from one reading before - * swapping to another. - * - * This is more a 'prioritize blocks from one over the other', than a 'read all - * the blocks from one, then all the blocks from the other'. If you need that, - * look at {@link SerialBlockReader}. - * - * @author bjculkin - * - */ -public class LayeredBlockReader implements BlockReader { - /* - * The readers to drain from. - */ - private final BlockReader first; - private final BlockReader second; - - /* - * The current block number. - */ - private int blockNo; - - /** - * Create a new layered block reader. - * - * @param primary - * The first source to read blocks from. - * - * @param secondary - * The second source to read blocks from. - */ - public LayeredBlockReader(final BlockReader primary, final BlockReader secondary) { - first = primary; - second = secondary; - } - - @Override - public boolean hasNextBlock() { - return first.hasNextBlock() || second.hasNextBlock(); - } - - @Override - public Block getBlock() { - final Block firstBlock = first.getBlock(); - - /* - * Only drain a block from the second reader if none are - * available in the first reader. - */ - return firstBlock == null ? second.getBlock() : firstBlock; - } - - @Override - public boolean nextBlock() { - final boolean gotFirst = first.nextBlock(); - final boolean succ = gotFirst ? gotFirst : second.nextBlock(); - - if (succ) { - blockNo += 1; - } - - return succ; - } - - @Override - public int getBlockCount() { - return blockNo; - } - - @Override - public void close() throws IOException { - second.close(); - - first.close(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java deleted file mode 100644 index 12fa848..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java +++ /dev/null @@ -1,54 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.IOException; - -import java.util.function.UnaryOperator; - -public class MappedBlockReader implements BlockReader { - private BlockReader reader; - - private Block current; - - private UnaryOperator transform; - - private int blockNo; - - public MappedBlockReader(BlockReader source, UnaryOperator trans) { - reader = source; - transform = trans; - - blockNo = 0; - } - - @Override - public boolean hasNextBlock() { - return reader.hasNextBlock(); - } - - @Override - public Block getBlock() { - return current; - } - - @Override - public boolean nextBlock() { - if(hasNextBlock()) { - current = transform.apply(reader.next()); - blockNo += 1; - - return true; - } - - return false; - } - - @Override - public int getBlockCount() { - return blockNo; - } - - @Override - public void close() throws IOException { - reader.close(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java deleted file mode 100644 index 0cc9dea..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java +++ /dev/null @@ -1,106 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.IOException; -import java.util.Deque; -import java.util.LinkedList; - -/** - * A block reader that supports pushing blocks onto the input queue so that they - * are provided before blocks read from an input source. - * - * @author bjculkin - * - */ -public class PushbackBlockReader implements BlockReader { - private final BlockReader source; - - /* - * The queue of pushed-back blocks. - */ - private final Deque waiting; - - private Block curBlock; - - private int blockNo; - - /** - * Create a new pushback block reader. - * - * @param src - * The block reader to use when no blocks are queued. - */ - public PushbackBlockReader(final BlockReader src) { - source = src; - - waiting = new LinkedList<>(); - } - - @Override - public boolean hasNextBlock() { - return !waiting.isEmpty() || source.hasNextBlock(); - } - - @Override - public Block getBlock() { - return curBlock; - } - - @Override - public boolean nextBlock() { - /* - * Drain pushed-back blocks first. - */ - if (!waiting.isEmpty()) { - curBlock = waiting.pop(); - - blockNo += 1; - - return true; - } else { - final boolean succ = source.nextBlock(); - curBlock = source.getBlock(); - - if (succ) { - blockNo += 1; - } - - return succ; - } - } - - @Override - public int getBlockCount() { - return blockNo; - } - - @Override - public void close() throws IOException { - source.close(); - } - - /** - * Insert a block at the back of the queue of pending blocks. - * - * @param blk - * The block to put at the back. - */ - public void addBlock(final Block blk) { - waiting.add(blk); - } - - /** - * Insert a block at the front of the queue of pending blocks. - * - * @param blk - * The block to put at the front. - */ - public void pushBlock(final Block blk) { - waiting.push(blk); - } - - @Override - public String toString() { - return String.format("PushbackBlockReader [waiting=%s, curBlock=%s, blockNo=%s]", waiting, curBlock, - blockNo); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java deleted file mode 100644 index c229da1..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java +++ /dev/null @@ -1,102 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.IOException; -import java.util.Deque; - -/** - * Provides a means of concatenating two block readers. - * - * @author bjculkin - * - */ -public class SerialBlockReader implements BlockReader { - private Deque readerQueue; - - private int blockNo; - - /** - * Create a new serial block reader. - * - * @param readers - * The readers to pull from, in the order to pull from - * them. - */ - public SerialBlockReader(final BlockReader... readers) { - for (final BlockReader reader : readers) { - readerQueue.add(reader); - } - } - - @Override - public boolean hasNextBlock() { - if (readerQueue.isEmpty()) return false; - - /* - * Attempt to get a block from the first reader. - */ - boolean hasBlock = readerQueue.peek().hasNextBlock(); - boolean cont = hasBlock || readerQueue.isEmpty(); - - /* - * Close/dispose of readers until we get an open one. - */ - while (!cont) { - try { - readerQueue.pop().close(); - } catch (final IOException ioex) { - throw new IllegalStateException("Exception thrown by discarded reader", ioex); - } - - hasBlock = readerQueue.peek().hasNextBlock(); - cont = hasBlock || readerQueue.isEmpty(); - } - - return hasBlock; - } - - @Override - public Block getBlock() { - if (readerQueue.isEmpty()) - return null; - else return readerQueue.peek().getBlock(); - } - - @Override - public boolean nextBlock() { - if (readerQueue.isEmpty()) return false; - - boolean gotBlock = readerQueue.peek().nextBlock(); - boolean cont = gotBlock || readerQueue.isEmpty(); - - while (!cont) { - try { - readerQueue.pop().close(); - } catch (final IOException ioex) { - throw new IllegalStateException("Exception thrown by discarded reader", ioex); - } - - gotBlock = readerQueue.peek().nextBlock(); - cont = gotBlock || readerQueue.isEmpty(); - } - - if (cont) { - blockNo += 1; - } - - return cont; - } - - @Override - public int getBlockCount() { - return blockNo; - } - - @Override - public void close() throws IOException { - while (!readerQueue.isEmpty()) { - final BlockReader reader = readerQueue.pop(); - - reader.close(); - } - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java deleted file mode 100644 index 734bde8..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java +++ /dev/null @@ -1,115 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.IOException; -import java.io.LineNumberReader; -import java.io.Reader; -import java.util.NoSuchElementException; -import java.util.Scanner; -import java.util.regex.Pattern; - -import bjc.utils.funcutils.StringUtils; -/** - * Simple implementation of {@link BlockReader} - * - * NOTE: The EOF marker is always treated as a delimiter. You are expected to - * handle blocks that may be shorter than you expect. - * - * @author EVE - * - */ -public class SimpleBlockReader implements BlockReader { - /* - * I/O source for blocks. - */ - private final Scanner blockReader; - - /* - * The current block. - */ - private Block currBlock; - - /* - * Info about the current block. - */ - private int blockNo; - private int lineNo; - - /** - * Create a new block reader. - * - * @param blockDelim - * The pattern that separates blocks. Note that the end - * of file is always considered to end a block. - * - * @param source - * The source to read blocks from. - */ - public SimpleBlockReader(final String blockDelim, final Reader source) { - blockReader = new Scanner(source); - - final String pattern = String.format("(?:%s)|\\Z", blockDelim); - final Pattern pt = Pattern.compile(pattern, Pattern.MULTILINE); - - blockReader.useDelimiter(pt); - - lineNo = 1; - } - - @Override - public boolean hasNextBlock() { - return blockReader.hasNext(); - } - - @Override - public Block getBlock() { - return currBlock; - } - - @Override - public boolean nextBlock() { - try { - /* - * Read in a new block, and keep the line numbers sane. - */ - final int blockStartLine = lineNo; - final String blockContents = blockReader.next(); - final int blockEndLine = lineNo + StringUtils.countMatches(blockContents, "\\R"); - - lineNo = blockEndLine; - blockNo += 1; - - currBlock = new Block(blockNo, blockContents, blockStartLine, blockEndLine); - - return true; - } catch (final NoSuchElementException nseex) { - currBlock = null; - - return false; - } - } - - @Override - public int getBlockCount() { - return blockNo; - } - - @Override - public void close() throws IOException { - blockReader.close(); - } - - /** - * Set the delimiter used to separate blocks. - * - * @param delim - * The delimiter used to separate blocks. - */ - public void setDelimiter(final String delim) { - blockReader.useDelimiter(delim); - } - - @Override - public String toString() { - return String.format("SimpleBlockReader [currBlock=%s, blockNo=%s]", currBlock, blockNo); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/ToggledBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/ToggledBlockReader.java deleted file mode 100644 index 8f39b8f..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/ToggledBlockReader.java +++ /dev/null @@ -1,63 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.IOException; - -import bjc.utils.data.BooleanToggle; - -public class ToggledBlockReader implements BlockReader { - private BlockReader leftSource; - private BlockReader rightSource; - - /* - * We choose the left source when this is true. - */ - private BooleanToggle leftToggle; - - private int blockNo; - - public ToggledBlockReader(BlockReader left, BlockReader right) { - leftSource = left; - rightSource = right; - - blockNo = 0; - - leftToggle = new BooleanToggle(); - } - - @Override - public boolean hasNextBlock() { - if(leftToggle.peek()) return leftSource.hasNextBlock(); - else return rightSource.hasNextBlock(); - } - - @Override - public Block getBlock() { - if(leftToggle.peek()) return leftSource.getBlock(); - else return rightSource.getBlock(); - } - - @Override - public boolean nextBlock() { - boolean succ; - - if(leftToggle.get()) { - succ = leftSource.nextBlock(); - } else { - succ = rightSource.nextBlock(); - } - - if(succ) blockNo += 1; - return succ; - } - - @Override - public int getBlockCount() { - return blockNo; - } - - @Override - public void close() throws IOException { - leftSource.close(); - rightSource.close(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java deleted file mode 100644 index 3a1e393..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java +++ /dev/null @@ -1,70 +0,0 @@ -package bjc.utils.ioutils.blocks; - -import java.io.IOException; - -/** - * A block reader that fires an action before a block is actually read. - * - * @author bjculkin - * - */ -public class TriggeredBlockReader implements BlockReader { - private final BlockReader source; - - private int blockNo; - - /* - * The action to fire. - */ - private final Runnable action; - - /** - * Create a new triggered reader with the specified source/action. - * - * @param source - * The block reader to read blocks from. - * - * @param action - * The action to execute before reading a block. - */ - public TriggeredBlockReader(final BlockReader source, final Runnable action) { - this.source = source; - this.action = action; - - blockNo = 0; - } - - @Override - public boolean hasNextBlock() { - action.run(); - - return source.hasNextBlock(); - } - - @Override - public Block getBlock() { - return source.getBlock(); - } - - @Override - public boolean nextBlock() { - blockNo += 1; - - return source.nextBlock(); - } - - @Override - public int getBlockCount() { - return blockNo; - } - - @Override - public void close() throws IOException { - source.close(); - } - - @Override - public String toString() { - return String.format("TriggeredBlockReader [source=%s]", source); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java deleted file mode 100644 index a885808..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/DoubleMatcher.java +++ /dev/null @@ -1,46 +0,0 @@ -package bjc.utils.parserutils; - -import static bjc.utils.PropertyDB.applyFormat; -import static bjc.utils.PropertyDB.getRegex; - -import java.util.regex.Pattern; - -/* - * Checks if a string would pass Double.parseDouble. - * - * Uses a regex from the javadoc for Double.valueOf() - */ -class DoubleMatcher { - /* - * Unit pieces. - */ - private static final String rDecDigits = getRegex("fpDigits"); - private static final String rHexDigits = getRegex("fpHexDigits"); - private static final String rExponent = applyFormat("fpExponent", getRegex("fpExponent"), rDecDigits); - - /* - * Decimal floating point numbers. - */ - private static final String rSimpleDec = applyFormat("fpDecimalDecimal", rDecDigits, rExponent); - private static final String rSimpleIntDec = applyFormat("fpDecimalInteger", rDecDigits, rExponent); - - /* - * Hex floating point numbers. - */ - private static final String rHexInt = applyFormat("fpHexInteger", rHexDigits); - private static final String rHexDec = applyFormat("fpHexDecimal", rHexDigits); - private static final String rHexLead = applyFormat("fpHexLeader", rHexInt, rHexDec); - private static final String rHexString = applyFormat("fpHexString", rHexLead, rDecDigits); - - /* - * Floating point components. - */ - private static final String rFPLeader = getRegex("fpLeader"); - private static final String rFPNum = applyFormat("fpNumber", rSimpleIntDec, rSimpleDec, rHexString); - - /* - * Full double. - */ - private static final String rDouble = applyFormat("fpDouble", rFPLeader, rFPNum); - public static final Pattern doubleLiteral = Pattern.compile("\\A" + rDouble + "\\Z"); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/IPrecedent.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/IPrecedent.java deleted file mode 100644 index aa366cf..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/IPrecedent.java +++ /dev/null @@ -1,28 +0,0 @@ -package bjc.utils.parserutils; - -/** - * Represents something that has a set precedence - * - * @author ben - * - */ -@FunctionalInterface -public interface IPrecedent { - /** - * Create a new object with set precedence - * - * @param precedence - * The precedence of the object to handle - * @return A new object with set precedence - */ - public static IPrecedent newSimplePrecedent(final int precedence) { - return () -> precedence; - } - - /** - * Get the precedence of the attached object - * - * @return The precedence of the attached object - */ - public int getPrecedence(); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ParserException.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/ParserException.java deleted file mode 100644 index ae33aba..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ParserException.java +++ /dev/null @@ -1,36 +0,0 @@ -package bjc.utils.parserutils; - -/** - * General superclass for exceptions thrown during parsing. - * - * @author EVE - * - */ -public class ParserException extends Exception { - /** - * - */ - private static final long serialVersionUID = 631298568113373233L; - - /** - * Create a new exception with the provided message. - * - * @param msg - * The message for the exception. - */ - public ParserException(final String msg) { - super(msg); - } - - /** - * Create a new exception with the provided message and cause. - * - * @param msg - * The message for the exception. - * @param cause - * The cause of the exception. - */ - public ParserException(final String msg, final Exception cause) { - super(msg, cause); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java deleted file mode 100644 index a1b5feb..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java +++ /dev/null @@ -1,274 +0,0 @@ -package bjc.utils.parserutils; - -import java.util.Deque; -import java.util.LinkedList; -import java.util.function.Consumer; -import java.util.function.Function; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.FunctionalMap; -import bjc.utils.funcdata.IList; -import bjc.utils.funcdata.IMap; -import bjc.utils.funcutils.StringUtils; - -/** - * Utility to run the shunting yard algorithm on a bunch of tokens. - * - * @author ben - * - * @param - * The type of tokens being shunted. - */ -public class ShuntingYard { - /** - * A enum representing the fundamental operator types. - * - * @author ben - * - */ - public static enum Operator implements IPrecedent { - /** - * Represents addition. - */ - ADD(1), - /** - * Represents subtraction. - */ - SUBTRACT(2), - - /** - * Represents multiplication. - */ - MULTIPLY(3), - /** - * Represents division. - */ - DIVIDE(4); - - private final int precedence; - - private Operator(final int prec) { - precedence = prec; - } - - @Override - public int getPrecedence() { - return precedence; - } - } - - /* - * Function that shunts tokens. - */ - private final class TokenShunter implements Consumer { - private final IList output; - private final Deque stack; - private final Function transformer; - - public TokenShunter(final IList outpt, final Deque stack, - final Function transformer) { - this.output = outpt; - this.stack = stack; - this.transformer = transformer; - } - - @Override - public void accept(final String token) { - /* - * Handle operators - */ - if (operators.containsKey(token)) { - /* - * Pop operators while there isn't a higher precedence one - */ - while (!stack.isEmpty() && isHigherPrec(token, stack.peek())) { - output.add(transformer.apply(stack.pop())); - } - - /* - * Put this operator onto the stack - */ - 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 - */ - final String swappedToken = token.replace(')', '('); - - /* - * Remove tokens up to a matching parenthesis - */ - while (!stack.peek().equals(swappedToken)) { - output.add(transformer.apply(stack.pop())); - } - - /* - * Remove the parenthesis - */ - stack.pop(); - } else { - /* - * Just add the transformed token - */ - output.add(transformer.apply(token)); - } - } - } - - /* - * Holds all the shuntable operations. - */ - private IMap operators; - - /** - * Create a new shunting yard with a default set of operators. - * - * @param configureBasics - * Whether or not basic math operators should be - * provided. - */ - public ShuntingYard(final boolean configureBasics) { - operators = new FunctionalMap<>(); - - /* - * Add basic operators if we're configured to do so - */ - if (configureBasics) { - operators.put("+", Operator.ADD); - operators.put("-", Operator.SUBTRACT); - operators.put("*", Operator.MULTIPLY); - operators.put("/", Operator.DIVIDE); - } - } - - /** - * Add an operator to the list of shuntable operators. - * - * @param operator - * The token representing the operator. - * - * @param precedence - * The precedence of the operator to add. - */ - public void addOp(final String operator, final int precedence) { - /* - * Create the precedence marker - */ - final IPrecedent prec = IPrecedent.newSimplePrecedent(precedence); - - this.addOp(operator, prec); - } - - /** - * Add an operator to the list of shuntable operators. - * - * @param operator - * The token representing the operator. - * - * @param precedence - * The precedence of the operator. - */ - public void addOp(final String operator, final IPrecedent precedence) { - /* - * Complain about trying to add an incorrect operator - */ - if (operator == null) - throw new NullPointerException("Operator must not be null"); - else if (precedence == null) throw new NullPointerException("Precedence must not be null"); - - /* - * Add the operator to the ones we handle - */ - operators.put(operator, precedence); - } - - private boolean isHigherPrec(final String left, final String right) { - /* - * Check if the right operator exists - */ - final boolean exists = operators.containsKey(right); - - /* - * If it doesn't, the left is higher precedence. - */ - if (!exists) return false; - - /* - * Get the precedence of operators - */ - final int rightPrecedence = operators.get(right).getPrecedence(); - final int leftPrecedence = operators.get(left).getPrecedence(); - - /* - * Evaluate what we were asked - */ - return rightPrecedence >= leftPrecedence; - } - - /** - * Transform a string of tokens from infix notation to postfix. - * - * @param input - * The string to transform. - * - * @param transformer - * The function to use to transform strings to tokens. - * - * @return A list of tokens in postfix notation. - */ - public IList postfix(final IList input, final Function transformer) { - /* - * Check our input - */ - if (input == null) - throw new NullPointerException("Input must not be null"); - else if (transformer == null) throw new NullPointerException("Transformer must not be null"); - - /* - * Here's what we're handing back - */ - final IList output = new FunctionalList<>(); - - /* - * The stack to put operators on - */ - final Deque stack = new LinkedList<>(); - - /* - * Shunt the tokens - */ - input.forEach(new TokenShunter(output, stack, transformer)); - - /* - * Transform any resulting tokens - */ - stack.forEach(token -> { - output.add(transformer.apply(token)); - }); - - return output; - } - - /** - * Remove an operator from the list of shuntable operators. - * - * @param operator - * The token representing the operator. If null, remove - * all operators. - */ - public void removeOp(final String operator) { - /* - * Check if we want to remove all operators - */ - if (operator == null) { - operators = new FunctionalMap<>(); - } else { - operators.remove(operator); - } - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDescaper.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDescaper.java deleted file mode 100644 index 096656a..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDescaper.java +++ /dev/null @@ -1,242 +0,0 @@ -package bjc.utils.parserutils; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.UnaryOperator; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -import static java.util.Map.Entry; - -import static bjc.utils.PropertyDB.applyFormat; -import static bjc.utils.PropertyDB.getCompiledRegex; -import static bjc.utils.PropertyDB.getRegex; - -public class StringDescaper { - private Logger LOGGER = Logger.getLogger(StringDescaper.class.getName()); - - /* - * Patterns and pattern parts. - */ - private static String rPossibleEscapeString = getRegex("possibleStringEscape"); - private static Pattern possibleEscapePatt = Pattern.compile(rPossibleEscapeString); - - private static String rShortEscape = getRegex("shortFormStringEscape"); - private static String rOctalEscape = getRegex("octalStringEscape"); - private static String rUnicodeEscape = getRegex("unicodeStringEscape"); - - private String rEscapeString; - private Pattern escapePatt; - - private static String rDoubleQuoteString = applyFormat("doubleQuotes", getRegex("nonStringEscape"), rPossibleEscapeString); - private static Pattern doubleQuotePatt = Pattern.compile(rDoubleQuoteString); - - private static Pattern quotePatt = getCompiledRegex("unescapedQuote"); - - private Map literalEscapes; - private Map> specialEscapes; - - public StringDescaper() { - literalEscapes = new HashMap<>(); - specialEscapes = new HashMap<>(); - - rEscapeString = String.format("\\\\(%1$s|%2$s|%3$s)"); - escapePatt = Pattern.compile(rEscapeString); - } - - public void addLiteralEscape(String escape, String val) { - if(literalEscapes.containsKey(escape)) { - LOGGER.warning(String.format("Shadowing literal escape '%s'\n", escape)); - } - - literalEscapes.put(escape, val); - } - - public void addSpecialEscape(String escape, UnaryOperator val) { - if(specialEscapes.containsKey(escape)) { - LOGGER.warning(String.format("Shadowing special escape '%s'\n", escape)); - } - - /* - * Make sure this special escape is a valid regex. - */ - - Pattern patt = null; - try { - patt = Pattern.compile(escape); - } catch (PatternSyntaxException psex) { - String msg = String.format("Invalid special escape '%s'", escape); - - IllegalArgumentException iaex = new IllegalArgumentException(msg); - iaex.initCause(psex); - - throw psex; - } - - specialEscapes.put(patt, val); - } - - public void compileEscapes() { - StringBuilder work = new StringBuilder(); - - for(String litEscape : literalEscapes.keySet()) { - work.append("|(?:"); - work.append(Pattern.quote(litEscape)); - work.append(")"); - } - - for(Pattern specEscape : specialEscapes.keySet()) { - work.append("|(?:"); - work.append(specEscape.toString()); - work.append(")"); - } - - /* - * Convert user-defined escapes to a regex for matching. - * We don't need a bar before %4 because the string has it. - */ - rEscapeString = String.format("\\(%1$s|%2$s|%3$s%4$s)", rShortEscape, rOctalEscape, rUnicodeEscape, work.toString()); - escapePatt = Pattern.compile(rEscapeString); - } - - /** - * Replace escape characters with their actual equivalents. - * - * @param inp - * The string to replace escape sequences in. - * - * @return The string with escape sequences replaced by their equivalent - * characters. - */ - public String descapeString(final String inp) { - if (inp == null) { - throw new NullPointerException("Input to descapeString must not be null"); - } - - /* - * Prepare the buffer and escape finder. - */ - final StringBuffer work = new StringBuffer(); - final Matcher possibleEscapeFinder = possibleEscapePatt.matcher(inp); - final Matcher escapeFinder = escapePatt.matcher(inp); - - while (possibleEscapeFinder.find()) { - if (!escapeFinder.find()) { - /* - * Found a possible escape that isn't actually an - * escape. - */ - final String msg = String.format("Illegal escape sequence '%s' at position %d of string '%s'", - possibleEscapeFinder.group(), possibleEscapeFinder.start(), inp); - throw new IllegalArgumentException(msg); - } - - final String escapeSeq = escapeFinder.group(); - - /* - * Convert the escape to a string. - */ - String escapeRep = ""; - switch (escapeSeq) { - case "\\b": - escapeRep = "\b"; - break; - case "\\t": - escapeRep = "\t"; - break; - case "\\n": - escapeRep = "\n"; - break; - case "\\f": - escapeRep = "\f"; - break; - case "\\r": - escapeRep = "\r"; - break; - case "\\\"": - escapeRep = "\""; - break; - case "\\'": - escapeRep = "'"; - break; - case "\\\\": - /* - * Skip past the second slash. - */ - possibleEscapeFinder.find(); - escapeRep = "\\"; - break; - default: - if (escapeSeq.startsWith("u")) { - escapeRep = handleUnicodeEscape(escapeSeq.substring(1)); - } else if(escapeSeq.startsWith("O")) { - escapeRep = handleOctalEscape(escapeSeq.substring(1)); - } else if(literalEscapes.containsKey(escapeSeq)) { - escapeRep = literalEscapes.get(escapeSeq); - } else { - for(Entry> ent : specialEscapes.entrySet()) { - Pattern pat = ent.getKey(); - - Matcher mat = pat.matcher(escapeSeq); - if(mat.matches()) { - escapeRep = ent.getValue().apply(escapeSeq); - break; - } - } - } - } - - escapeFinder.appendReplacement(work, escapeRep); - } - - escapeFinder.appendTail(work); - - return work.toString(); - } - - /* - * Handle a unicode codepoint. - */ - private static String handleUnicodeEscape(final String seq) { - try { - final int codepoint = Integer.parseInt(seq, 16); - - return new String(Character.toChars(codepoint)); - } catch (final IllegalArgumentException iaex) { - final String msg = String.format("'%s' is not a valid Unicode escape sequence'", seq); - - final IllegalArgumentException reiaex = new IllegalArgumentException(msg); - - reiaex.initCause(iaex); - - throw reiaex; - } - } - - /* - * Handle a octal codepoint. - */ - private static String handleOctalEscape(final String seq) { - try { - final int codepoint = Integer.parseInt(seq, 8); - - if (codepoint > 255) { - final String msg = String.format("'%d' is outside the range of octal escapes', codepoint"); - - throw new IllegalArgumentException(msg); - } - - return new String(Character.toChars(codepoint)); - } catch (final IllegalArgumentException iaex) { - final String msg = String.format("'%s' is not a valid octal escape sequence'", seq); - - final IllegalArgumentException reiaex = new IllegalArgumentException(msg); - - reiaex.initCause(iaex); - - throw reiaex; - } - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java deleted file mode 100644 index 30ccc5a..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenTransformer.java +++ /dev/null @@ -1,131 +0,0 @@ -package bjc.utils.parserutils; - -import java.util.Deque; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; - -import bjc.utils.data.IHolder; -import bjc.utils.data.ITree; -import bjc.utils.data.Pair; -import bjc.utils.data.Tree; -import bjc.utils.parserutils.TreeConstructor.ConstructorState; -import bjc.utils.parserutils.TreeConstructor.QueueFlattener; - -/* - * Handle creating ASTs from tokens. - */ -final class TokenTransformer implements Consumer { - /* - * Handle operators - */ - private final class OperatorHandler implements UnaryOperator> { - private final TokenType element; - - public OperatorHandler(final TokenType element) { - this.element = element; - } - - @Override - public ConstructorState apply(final ConstructorState pair) { - /* - * Replace the current AST with the result of handling an operator - */ - return new ConstructorState<>(pair.bindLeft(queuedASTs -> { - return handleOperator(queuedASTs); - })); - } - - private ConstructorState handleOperator(final Deque> queuedASTs) { - /* - * The AST we're going to hand back - */ - ITree newAST; - - /* - * Handle special operators - */ - if (isSpecialOperator.test(element)) { - newAST = handleSpecialOperator.apply(element).apply(queuedASTs); - } else { - /* - * Error if we don't have enough for a binary operator - */ - if (queuedASTs.size() < 2) { - final String msg = String.format( - "Attempted to parse binary operator without enough operands\n\tProblem operator is: %s\n\tPossible operand is: %s", - element.toString(), queuedASTs.peek().toString()); - - throw new IllegalStateException(msg); - } - - /* - * Grab the two operands - */ - final ITree right = queuedASTs.pop(); - final ITree left = queuedASTs.pop(); - - /* - * Create a new AST - */ - newAST = new Tree<>(element, left, right); - } - - /* - * Stick it onto the stack - */ - queuedASTs.push(newAST); - - /* - * Hand back the state - */ - return new ConstructorState<>(queuedASTs, newAST); - } - } - - private final IHolder> initialState; - - private final Predicate operatorPredicate; - - private final Predicate isSpecialOperator; - private final Function> handleSpecialOperator; - - /* - * Create a new transformer - */ - public TokenTransformer(final IHolder> initialState, - final Predicate operatorPredicate, final Predicate isSpecialOperator, - final Function> handleSpecialOperator) { - this.initialState = initialState; - this.operatorPredicate = operatorPredicate; - this.isSpecialOperator = isSpecialOperator; - this.handleSpecialOperator = handleSpecialOperator; - } - - @Override - public void accept(final TokenType element) { - /* - * Handle operators - */ - if (operatorPredicate.test(element)) { - initialState.transform(new OperatorHandler(element)); - } else { - final ITree newAST = new Tree<>(element); - - /* - * Insert the new tree into the AST - */ - initialState.transform(pair -> { - /* - * Transform the pair, ignoring the current AST in favor of the one consisting of the current element - */ - return new ConstructorState<>(pair.bindLeft(queue -> { - queue.push(newAST); - - return new Pair<>(queue, newAST); - })); - }); - } - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java deleted file mode 100644 index 67c1e5a..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TokenUtils.java +++ /dev/null @@ -1,303 +0,0 @@ -package bjc.utils.parserutils; - -import static bjc.utils.PropertyDB.applyFormat; -import static bjc.utils.PropertyDB.getCompiledRegex; -import static bjc.utils.PropertyDB.getRegex; - -import java.util.LinkedList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; -import bjc.utils.parserutils.splitter.TokenSplitter; - -/** - * Utilities useful for operating on PL tokens. - * - * @author EVE - * - */ -public class TokenUtils { - /** - * Simple implementation of TokenSplitter for removing double-quoted - * strings. - * - * @author EVE - * - */ - public static class StringTokenSplitter implements TokenSplitter { - @Override - public IList split(final String input) { - return new FunctionalList<>(TokenUtils.removeDQuotedStrings(input)); - } - } - - /* - * Patterns and pattern parts. - */ - private static String rPossibleEscapeString = getRegex("possibleStringEscape"); - - private static Pattern possibleEscapePatt = Pattern.compile(rPossibleEscapeString); - - private static String rShortEscape = getRegex("shortFormStringEscape"); - private static String rOctalEscape = getRegex("octalStringEscape"); - private static String rUnicodeEscape = getRegex("unicodeStringEscape"); - - private static String rEscapeString = applyFormat("stringEscape", rShortEscape, rOctalEscape, rUnicodeEscape); - - private static Pattern escapePatt = Pattern.compile(rEscapeString); - - private static String rDoubleQuoteString = applyFormat("doubleQuotes", getRegex("nonStringEscape"), - rPossibleEscapeString); - - private static Pattern doubleQuotePatt = Pattern.compile(rDoubleQuoteString); - - private static Pattern quotePatt = getCompiledRegex("unescapedQuote"); - - private static Pattern intLitPattern = getCompiledRegex("intLiteral"); - - /** - * Remove double quoted strings from a string. - * - * Splits a string around instances of java-style double-quoted strings. - * - * @param inp - * The string to split. - * - * @return An list containing alternating bits of the string and the - * embedded double-quoted strings that separated them. - */ - public static List removeDQuotedStrings(final String inp) { - if (inp == null) throw new NullPointerException("inp must not be null"); - - /* - * What we need for piece-by-piece string building - */ - StringBuffer work = new StringBuffer(); - final List res = new LinkedList<>(); - - /* - * Matcher for proper strings and single quotes. - */ - final Matcher mt = doubleQuotePatt.matcher(inp); - final Matcher corr = quotePatt.matcher(inp); - - if (corr.find() && !corr.find()) { - /* - * There's a unmatched opening quote with no strings. - */ - final String msg = String.format( - "Unclosed string literal '%s'. Opening quote was at position %d", inp, - inp.indexOf("\"")); - - throw new IllegalArgumentException(msg); - } - - while (mt.find()) { - /* - * Remove the string until the quoted string. - */ - mt.appendReplacement(work, ""); - - /* - * Add the string preceding the double-quoted string and - * the double-quoted string to the list. - */ - res.add(work.toString()); - res.add(mt.group(1)); - - /* - * Renew the buffer. - */ - work = new StringBuffer(); - } - - /* - * Grab the remainder of the string. - */ - mt.appendTail(work); - final String tail = work.toString(); - - if (tail.contains("\"")) { - /* - * There's a unmatched opening quote with at least one - * string. - */ - final String msg = String.format( - "Unclosed string literal '%s'. Opening quote was at position %d", inp, - inp.lastIndexOf("\"")); - - throw new IllegalArgumentException(msg); - } - - /* - * Only add an empty tail if the string was empty. - */ - if (!tail.equals("") || res.isEmpty()) { - res.add(tail); - } - - return res; - } - - /** - * Replace escape characters with their actual equivalents. - * - * @param inp - * The string to replace escape sequences in. - * - * @return The string with escape sequences replaced by their equivalent - * characters. - */ - public static String descapeString(final String inp) { - if (inp == null) throw new NullPointerException("inp must not be null"); - - /* - * Prepare the buffer and escape finder. - */ - final StringBuffer work = new StringBuffer(); - final Matcher possibleEscapeFinder = possibleEscapePatt.matcher(inp); - final Matcher escapeFinder = escapePatt.matcher(inp); - - while (possibleEscapeFinder.find()) { - if (!escapeFinder.find()) { - /* - * Found a possible escape that isn't actually an - * escape. - */ - final String msg = String.format("Illegal escape sequence '%s' at position %d", - possibleEscapeFinder.group(), possibleEscapeFinder.start()); - - throw new IllegalArgumentException(msg); - } - - final String escapeSeq = escapeFinder.group(); - - /* - * Convert the escape to a string. - */ - String escapeRep = ""; - switch (escapeSeq) { - case "\\b": - escapeRep = "\b"; - break; - case "\\t": - escapeRep = "\t"; - break; - case "\\n": - escapeRep = "\n"; - break; - case "\\f": - escapeRep = "\f"; - break; - case "\\r": - escapeRep = "\r"; - break; - case "\\\"": - escapeRep = "\""; - break; - case "\\'": - escapeRep = "'"; - break; - case "\\\\": - /* - * Skip past the second slash. - */ - possibleEscapeFinder.find(); - escapeRep = "\\"; - break; - default: - if (escapeSeq.startsWith("u")) { - escapeRep = handleUnicodeEscape(escapeSeq.substring(1)); - } else { - escapeRep = handleOctalEscape(escapeSeq); - } - } - - escapeFinder.appendReplacement(work, escapeRep); - } - - escapeFinder.appendTail(work); - - return work.toString(); - } - - /* - * Handle a unicode codepoint. - */ - private static String handleUnicodeEscape(final String seq) { - try { - final int codepoint = Integer.parseInt(seq, 16); - - return new String(Character.toChars(codepoint)); - } catch (final IllegalArgumentException iaex) { - final String msg = String.format("'%s' is not a valid Unicode escape sequence'", seq); - - final IllegalArgumentException reiaex = new IllegalArgumentException(msg); - - reiaex.initCause(iaex); - - throw reiaex; - } - } - - /* - * Handle a octal codepoint. - */ - private static String handleOctalEscape(final String seq) { - try { - final int codepoint = Integer.parseInt(seq, 8); - - if (codepoint > 255) { - final String msg = String - .format("'%d' is outside the range of octal escapes', codepoint"); - - throw new IllegalArgumentException(msg); - } - - return new String(Character.toChars(codepoint)); - } catch (final IllegalArgumentException iaex) { - final String msg = String.format("'%s' is not a valid octal escape sequence'", seq); - - final IllegalArgumentException reiaex = new IllegalArgumentException(msg); - - reiaex.initCause(iaex); - - throw reiaex; - } - } - - /** - * Check if a given string would be successfully converted to a double - * by {@link Double#parseDouble(String)}. - * - * @param inp - * The string to check. - * @return Whether the string is a valid double or not. - */ - public static boolean isDouble(final String inp) { - return DoubleMatcher.doubleLiteral.matcher(inp).matches(); - } - - /** - * Check if a given string would be successfully converted to a integer - * by {@link Integer#parseInt(String)}. - * - * NOTE: This only checks syntax. Using values out of the range of - * integers will still cause errors. - * - * @param inp - * The input to check. - * @return Whether the string is a valid integer or not. - */ - public static boolean isInt(final String inp) { - try { - Integer.parseInt(inp); - return true; - } catch (NumberFormatException nfex) { - return false; - } - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java deleted file mode 100644 index 90141ef..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java +++ /dev/null @@ -1,125 +0,0 @@ -package bjc.utils.parserutils; - -import java.util.Deque; -import java.util.LinkedList; -import java.util.function.Function; -import java.util.function.Predicate; - -import bjc.utils.data.IHolder; -import bjc.utils.data.IPair; -import bjc.utils.data.ITree; -import bjc.utils.data.Identity; -import bjc.utils.data.Pair; -import bjc.utils.funcdata.IList; - -/** - * Creates a parse tree from a postfix expression - * - * @author ben - * - */ -public class TreeConstructor { - /** - * Alias interface for special operator types. - * - * @param - * The token type of the tree. - */ - public interface QueueFlattener extends Function>, ITree> { - - } - - /* - * Alias for constructor state. - */ - static final class ConstructorState extends Pair>, ITree> { - public ConstructorState(final Deque> left, final ITree right) { - super(left, right); - } - - public ConstructorState(final IPair>, ITree> par) { - super(par.getLeft(), par.getRight()); - } - } - - /** - * Construct a tree from a list of tokens in postfix notation - * - * Only binary operators are accepted. - * - * @param - * The elements of the parse tree - * @param tokens - * The list of tokens to build a tree from - * @param isOperator - * The predicate to use to determine if something is a - * operator - * @return A AST from the expression - */ - public static ITree constructTree(final IList tokens, - final Predicate isOperator) { - /* - * Construct a tree with no special operators - */ - return constructTree(tokens, isOperator, op -> false, null); - } - - /** - * Construct a tree from a list of tokens in postfix notation. - * - * Only binary operators are accepted by default. Use the last two - * parameters to handle non-binary operators. - * - * @param - * The elements of the parse tree. - * - * @param tokens - * The list of tokens to build a tree from. - * - * @param isOperator - * The predicate to use to determine if something is a - * operator. - * - * @param isSpecialOperator - * The predicate to use to determine if an operator needs - * special handling. - * - * @param handleSpecialOperator - * The function to use to handle special case operators. - * - * @return A AST from the expression - * - */ - public static ITree constructTree(final IList tokens, - final Predicate isOperator, final Predicate isSpecialOperator, - final Function> handleSpecialOperator) { - /* - * Make sure our parameters are valid - */ - if (tokens == null) - throw new NullPointerException("Tokens must not be null"); - else if (isOperator == null) - throw new NullPointerException("Operator predicate must not be null"); - else if (isSpecialOperator == null) - throw new NullPointerException("Special operator determiner must not be null"); - - /* - * Here is the state for the tree construction - */ - final IHolder> initialState = new Identity<>( - new ConstructorState<>(new LinkedList<>(), null)); - - /* - * Transform each of the tokens - */ - tokens.forEach(new TokenTransformer<>(initialState, isOperator, isSpecialOperator, - handleSpecialOperator)); - - /* - * Grab the tree from the state - */ - return initialState.unwrap(pair -> { - return pair.getRight(); - }); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/defines/IteratedDefine.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/defines/IteratedDefine.java deleted file mode 100644 index 552b471..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/defines/IteratedDefine.java +++ /dev/null @@ -1,48 +0,0 @@ -package bjc.utils.parserutils.defines; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.function.UnaryOperator; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import bjc.utils.data.CircularIterator; - -public class IteratedDefine implements UnaryOperator { - private Pattern patt; - - private Iterator repls; - - /** - * Create a new iterated define. - * - * @param pattern - * The pattern to use for matching. - * @param circular - * Whether or not to loop through the list of replacers, or just - * repeat the last one. - * @param replacers - * The set of replacers to use. - */ - public IteratedDefine(Pattern pattern, boolean circular, String... replacers) { - patt = pattern; - - repls = new CircularIterator<>(Arrays.asList(replacers), circular); - } - - @Override - public String apply(String ln) { - Matcher mat = patt.matcher(ln); - StringBuffer sb = new StringBuffer(); - - while(mat.find()) { - String repl = repls.next(); - - mat.appendReplacement(sb, repl); - } - - mat.appendTail(sb); - - return sb.toString(); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/defines/SimpleDefine.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/defines/SimpleDefine.java deleted file mode 100644 index 42866c2..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/defines/SimpleDefine.java +++ /dev/null @@ -1,23 +0,0 @@ -package bjc.utils.parserutils.defines; - -import java.util.function.UnaryOperator; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class SimpleDefine implements UnaryOperator { - private Pattern patt; - private String repl; - - public SimpleDefine(Pattern pattern, String replace) { - patt = pattern; - - repl = replace; - } - - @Override - public String apply(String line) { - Matcher mat = patt.matcher(line); - - return mat.replaceAll(repl); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterException.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterException.java deleted file mode 100644 index 071afb4..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterException.java +++ /dev/null @@ -1,21 +0,0 @@ -package bjc.utils.parserutils.delims; - -/** - * The superclass for exceptions thrown during sequence delimitation. - */ -public class DelimiterException extends RuntimeException { - /** - * - */ - private static final long serialVersionUID = 2079514406049040888L; - - /** - * Create a new generic delimiter exception. - * - * @param res - * The reason for this exception. - */ - public DelimiterException(final String res) { - super(res); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java deleted file mode 100644 index b1d8597..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java +++ /dev/null @@ -1,593 +0,0 @@ -package bjc.utils.parserutils.delims; - -import java.util.Arrays; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.BiPredicate; -import java.util.function.Function; - -import bjc.utils.data.IPair; -import bjc.utils.data.ITree; -import bjc.utils.data.Pair; -import bjc.utils.data.Tree; -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * Represents a possible delimiter group to match. - * - * @author EVE - * - * @param - * The type of items in the sequence. - */ -public class DelimiterGroup { - /** - * Represents an instance of a delimiter group. - * - * @author EVE - * - */ - public class OpenGroup { - /* - * The contents of this group. - */ - private final Deque> contents; - - /* - * The contents of the current subgroup. - */ - private IList> currentGroup; - - /* - * The token that opened the group, and any opening parameters. - */ - private final T opener; - private final T[] params; - - /** - * Create a new instance of a delimiter group. - * - * @param open - * The item that opened this group. - * - * @param parms - * Any parameters from the opener. - */ - public OpenGroup(final T open, final T[] parms) { - opener = open; - params = parms; - - contents = new LinkedList<>(); - - currentGroup = new FunctionalList<>(); - } - - /** - * Add an item to this group instance. - * - * @param itm - * The item to add to this group instance. - */ - public void addItem(final ITree itm) { - currentGroup.add(itm); - } - - /** - * Mark a subgroup. - * - * @param marker - * The item that indicated this subgroup. - * - * @param chars - * The characteristics for building the tree. - */ - public void markSubgroup(final T marker, final SequenceCharacteristics chars) { - /* - * Add all of the contents to the subgroup. - */ - final ITree subgroupContents = new Tree<>(chars.contents); - for (final ITree itm : currentGroup) { - subgroupContents.addChild(itm); - } - - /* - * Handle subordinate sub-groups. - */ - while (!contents.isEmpty()) { - final ITree possibleSubordinate = contents.peek(); - - /* - * Subordinate lower priority subgroups. - */ - if (possibleSubordinate.getHead().equals(chars.subgroup)) { - final T otherMarker = possibleSubordinate.getChild(1).getHead(); - - if (subgroups.get(marker) > subgroups.get(otherMarker)) { - subgroupContents.prependChild(contents.pop()); - } else { - break; - } - } else { - subgroupContents.prependChild(contents.pop()); - } - } - - final Tree subgroup = new Tree<>(chars.subgroup, subgroupContents, new Tree<>(marker)); - - contents.push(subgroup); - - currentGroup = new FunctionalList<>(); - } - - /** - * Convert this group into a tree. - * - * @param closer - * The item that closed this group. - * - * @param chars - * The characteristics for building the tree. - * - * @return This group as a tree. - */ - public ITree toTree(final T closer, final SequenceCharacteristics chars) { - /* - * Mark any implied subgroups. - */ - if (impliedSubgroups.containsKey(closer)) { - markSubgroup(impliedSubgroups.get(closer), chars); - } - - final ITree res = new Tree<>(chars.contents); - - /* - * Add either the contents of the current group, - * or subgroups if they're their. - */ - if (contents.isEmpty()) { - currentGroup.forEach(res::addChild); - } else { - while (!contents.isEmpty()) { - res.prependChild(contents.poll()); - } - - currentGroup.forEach(res::addChild); - } - - return new Tree<>(groupName, new Tree<>(opener), res, new Tree<>(closer)); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - - builder.append("OpenGroup [contents="); - builder.append(contents); - builder.append(", currentGroup="); - builder.append(currentGroup); - builder.append(", opener="); - builder.append(opener); - builder.append("]"); - - return builder.toString(); - } - - /** - * Check if a group is excluded at the top level of this group. - * - * @param groupName - * The group to check. - * - * @return Whether or not the provided group is excluded. - */ - public boolean excludes(final T groupName) { - return topLevelExclusions.contains(groupName); - } - - /** - * Check if the provided delimiter would close this group. - * - * @param del - * The string to check as a closing delimiter. - * - * @return Whether or not the provided delimiter closes this - * group. - */ - public boolean isClosing(final T del) { - if (closingDelimiters.contains(del)) return true; - - for (final BiPredicate pred : predClosers) { - if (pred.test(del, params)) return true; - } - - return closingDelimiters.contains(del); - } - - /** - * Get the name of the group this is an instance of. - * - * @return The name of the group this is an instance of. - */ - public T getName() { - return groupName; - } - - /** - * Get the groups that aren't allowed at all in this group. - * - * @return The groups that aren't allowed at all in this group. - */ - public Set getNestingExclusions() { - return groupExclusions; - } - - /** - * Get the groups that are allowed to open anywhere inside this - * group. - * - * @return The groups allowed to open anywhere inside this - * group. - */ - public Map getNestingOpeners() { - return nestedOpenDelimiters; - } - - /** - * Checks if a given token marks a subgroup. - * - * @param tok - * The token to check. - * - * @return Whether or not the token marks a subgroup. - */ - public boolean marksSubgroup(final T tok) { - return subgroups.containsKey(tok); - } - - /** - * Checks if a given token opens a group. - * - * @param marker - * The token to check. - * - * @return The name of the group T opens, or null if it doesn't - * open one. - */ - public IPair doesOpen(final T marker) { - if (openDelimiters.containsKey(marker)) return new Pair<>(openDelimiters.get(marker), null); - - for (final Function> pred : predOpeners) { - final IPair par = pred.apply(marker); - - if (par.getLeft() != null) return par; - } - - return new Pair<>(null, null); - } - - /** - * Check if this group starts a new nesting scope. - * - * @return Whether this group starts a new nesting scope. - */ - public boolean isForgetful() { - return forgetful; - } - } - - /** - * The name of this delimiter group. - */ - public final T groupName; - - /* - * The delimiters that open groups at the top level of this group. - */ - private final Map openDelimiters; - - /* - * The delimiters that open groups inside of this group. - */ - private final Map nestedOpenDelimiters; - - /* - * The delimiters that close this group. - */ - private final Set closingDelimiters; - - /* - * The groups that can't occur in the top level of this group. - */ - private final Set topLevelExclusions; - - /* - * The groups that can't occur anywhere inside this group. - */ - private final Set groupExclusions; - - /* - * Mapping from sub-group delimiters, to any sub-groups enclosed in - * them. - */ - private final Map subgroups; - - /* - * Subgroups implied by a particular closing delimiter - */ - private final Map impliedSubgroups; - - /* - * Allows more complex openings - */ - private final List>> predOpeners; - - /* - * Allow more complex closings - */ - private final List> predClosers; - - /* - * Whether or not this group starts a new nesting set. - */ - private boolean forgetful; - - /** - * Create a new empty delimiter group. - * - * @param name - * The name of the delimiter group - */ - public DelimiterGroup(final T name) { - if (name == null) throw new NullPointerException("Group name must not be null"); - - groupName = name; - - openDelimiters = new HashMap<>(); - nestedOpenDelimiters = new HashMap<>(); - - closingDelimiters = new HashSet<>(); - - topLevelExclusions = new HashSet<>(); - groupExclusions = new HashSet<>(); - - subgroups = new HashMap<>(); - impliedSubgroups = new HashMap<>(); - - predOpeners = new LinkedList<>(); - predClosers = new LinkedList<>(); - } - - /** - * Adds one or more delimiters that close this group. - * - * @param closers - * Delimiters that close this group. - */ - @SafeVarargs - public final void addClosing(final T... closers) { - final List closerList = Arrays.asList(closers); - - for (final T closer : closerList) { - if (closer == null) - throw new NullPointerException("Closing delimiter must not be null"); - else if (closer.equals("")) - /* - * We can do this because equals works on - * arbitrary objects, not just those of the same - * type. - */ - throw new IllegalArgumentException("Empty string is not a valid exclusion"); - else { - closingDelimiters.add(closer); - } - } - } - - /** - * Adds one or more groups that cannot occur in the top level of this - * group. - * - * @param exclusions - * The groups forbidden in the top level of this group. - */ - @SafeVarargs - public final void addTopLevelForbid(final T... exclusions) { - for (final T exclusion : exclusions) { - if (exclusion == null) - throw new NullPointerException("Exclusion must not be null"); - else if (exclusion.equals("")) - /* - * We can do this because equals works on - * arbitrary objects, not just those of the same - * type. - */ - throw new IllegalArgumentException("Empty string is not a valid exclusion"); - else { - topLevelExclusions.add(exclusion); - } - } - } - - /** - * Adds one or more groups that cannot occur at all in this group. - * - * @param exclusions - * The groups forbidden inside this group. - */ - @SafeVarargs - public final void addGroupForbid(final T... exclusions) { - for (final T exclusion : exclusions) { - if (exclusion == null) - throw new NullPointerException("Exclusion must not be null"); - else if (exclusion.equals("")) - /* - * We can do this because equals works on - * arbitrary objects, not just those of the same - * type. - */ - throw new IllegalArgumentException("Empty string is not a valid exclusion"); - else { - groupExclusions.add(exclusion); - } - } - } - - /** - * Adds sub-group markers to this group. - * - * @param subgroup - * The token to mark a sub-group. - * - * @param priority - * The priority of this sub-group. - */ - public void addSubgroup(final T subgroup, final int priority) { - if (subgroup == null) throw new NullPointerException("Subgroup marker must not be null"); - - subgroups.put(subgroup, priority); - } - - /** - * Adds a marker that opens a group at the top level of this group. - * - * @param opener - * The marker that opens the group. - * - * @param group - * The group opened by the marker. - */ - public void addOpener(final T opener, final T group) { - if (opener == null) throw new NullPointerException("Opener must not be null"); - else if (group == null) throw new NullPointerException("Group to open must not be null"); - - openDelimiters.put(opener, group); - } - - /** - * Adds a marker that opens a group inside of this group. - * - * @param opener - * The marker that opens the group. - * - * @param group - * The group opened by the marker. - */ - public void addNestedOpener(final T opener, final T group) { - if (opener == null) throw new NullPointerException("Opener must not be null"); - else if (group == null) throw new NullPointerException("Group to open must not be null"); - - nestedOpenDelimiters.put(opener, group); - } - - /** - * Mark a closing delimiter as implying a subgroup. - * - * @param closer - * The closing delimiter. - * - * @param subgroup - * The subgroup to imply. - */ - public void implySubgroup(final T closer, final T subgroup) { - if (closer == null) throw new NullPointerException("Closer must not be null"); - else if (subgroup == null) throw new NullPointerException("Subgroup must not be null"); - else if (!closingDelimiters.contains(closer)) throw new IllegalArgumentException(String.format("No closing delimiter '%s' defined", closer)); - else if (!subgroups.containsKey(subgroup)) throw new IllegalArgumentException(String.format("No subgroup '%s' defined", subgroup)); - - impliedSubgroups.put(closer, subgroup); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - - builder.append("("); - - builder.append("groupName=["); - builder.append(groupName); - builder.append("], "); - - builder.append("closingDelimiters=["); - for (final T closer : closingDelimiters) { - builder.append(closer + ","); - } - builder.deleteCharAt(builder.length() - 1); - builder.append("]"); - - if (topLevelExclusions != null && !topLevelExclusions.isEmpty()) { - builder.append(", "); - builder.append("topLevelExclusions=["); - for (final T exclusion : topLevelExclusions) { - builder.append(exclusion + ","); - } - builder.deleteCharAt(builder.length() - 1); - builder.append("]"); - } - - if (groupExclusions != null && !groupExclusions.isEmpty()) { - builder.append(", "); - builder.append("groupExclusions=["); - for (final T exclusion : groupExclusions) { - builder.append(exclusion + ","); - } - builder.deleteCharAt(builder.length() - 1); - builder.append("]"); - } - - builder.append(" )"); - - return builder.toString(); - } - - /** - * Open an instance of this group. - * - * @param opener - * The item that opened this group. - * - * @param parms - * The parameters that opened this group - * - * @return An opened instance of this group. - */ - public OpenGroup open(final T opener, final T[] parms) { - return new OpenGroup(opener, parms); - } - - /** - * Adds a predicated opener to the top level of this group. - * - * @param pred - * The predicate that defines the opener and its - * parameters. - */ - public void addPredOpener(final Function> pred) { - predOpeners.add(pred); - } - - /** - * Adds a predicated closer to the top level of this group. - * - * @param pred - * The predicate that defines the closer. - */ - public void addPredCloser(final BiPredicate pred) { - predClosers.add(pred); - } - - /** - * Set whether or not this group starts a new nesting set. - * - * @param forgetful - * Whether this group starts a new nesting set. - */ - public void setForgetful(final boolean forgetful) { - this.forgetful = forgetful; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexCloser.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexCloser.java deleted file mode 100644 index 4b29949..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexCloser.java +++ /dev/null @@ -1,33 +0,0 @@ -package bjc.utils.parserutils.delims; - -import java.util.function.BiPredicate; - -/** - * A predicated closer for use with {@link RegexOpener}. - * - * @author bjculkin - * - */ -public class RegexCloser implements BiPredicate { - private final String rep; - - /** - * Create a new regex closer. - * - * @param closer - * The format string to use for closing. - */ - public RegexCloser(final String closer) { - rep = closer; - } - - @Override - public boolean test(final String closer, final String[] params) { - /* - * Confirm passing an array instead of a single var-arg. - */ - final String work = String.format(rep, (Object[]) params); - - return work.equals(closer); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java deleted file mode 100644 index ee93b73..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java +++ /dev/null @@ -1,54 +0,0 @@ -package bjc.utils.parserutils.delims; - -import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import bjc.utils.data.IPair; -import bjc.utils.data.Pair; - -/** - * A predicated opener for use with {@link RegexCloser} - * - * @author bjculkin - * - */ -public class RegexOpener implements Function> { - private final String name; - - private final Pattern patt; - - /** - * Create a new regex opener. - * - * @param groupName - * The name of the opened group. - * - * @param groupRegex - * The regex that matches the opener. - */ - public RegexOpener(final String groupName, final String groupRegex) { - name = groupName; - - patt = Pattern.compile(groupRegex); - } - - @Override - public IPair apply(final String str) { - final Matcher m = patt.matcher(str); - - if (m.matches()) { - final int numGroups = m.groupCount(); - - final String[] parms = new String[numGroups + 1]; - - for (int i = 0; i <= numGroups; i++) { - parms[i] = m.group(i); - } - - return new Pair<>(name, parms); - } - - return new Pair<>(null, null); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceCharacteristics.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceCharacteristics.java deleted file mode 100644 index 882b4c5..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceCharacteristics.java +++ /dev/null @@ -1,93 +0,0 @@ -package bjc.utils.parserutils.delims; - -/** - * Marks the parameters for building a sequence tree. - * - * @author EVE - * - * @param - * The type of item in the tree. - */ -public class SequenceCharacteristics { - /** - * The item to mark the root of the tree. - */ - public final T root; - - /** - * The item to mark the contents of a group/subgroup. - */ - - public final T contents; - - /** - * The item to mark a subgroup. - */ - public final T subgroup; - - /** - * Create a new set of parameters for building a tree. - * - * @param root - * The root marker. - * @param contents - * The group/subgroup contents marker. - * @param subgroup - * The subgroup marker. - */ - public SequenceCharacteristics(final T root, final T contents, final T subgroup) { - this.root = root; - this.contents = contents; - this.subgroup = subgroup; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - - result = prime * result + (contents == null ? 0 : contents.hashCode()); - result = prime * result + (root == null ? 0 : root.hashCode()); - result = prime * result + (subgroup == null ? 0 : subgroup.hashCode()); - - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof SequenceCharacteristics)) return false; - - final SequenceCharacteristics other = (SequenceCharacteristics) obj; - - if (contents == null) { - if (other.contents != null) return false; - } else if (!contents.equals(other.contents)) return false; - - if (root == null) { - if (other.root != null) return false; - } else if (!root.equals(other.root)) return false; - - if (subgroup == null) { - if (other.subgroup != null) return false; - } else if (!subgroup.equals(other.subgroup)) return false; - - return true; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - - builder.append("SequenceCharacteristics [root="); - builder.append(root == null ? "(null)" : root); - builder.append(", contents="); - builder.append(contents == null ? "(null)" : contents); - builder.append(", subgroup="); - builder.append(subgroup == null ? "(null)" : subgroup); - builder.append("]"); - - return builder.toString(); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java deleted file mode 100644 index ccfaffb..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java +++ /dev/null @@ -1,371 +0,0 @@ -package bjc.utils.parserutils.delims; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.HashMultiset; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multiset; - -import bjc.utils.data.IPair; -import bjc.utils.data.ITree; -import bjc.utils.data.Tree; -import bjc.utils.esodata.PushdownMap; -import bjc.utils.esodata.SimpleStack; -import bjc.utils.esodata.Stack; -import bjc.utils.funcdata.IMap; -import bjc.utils.funcutils.StringUtils; - -/** - * Convert linear sequences into trees that represent group structure. - * - * @author EVE - * - * @param - * The type of items in the sequence. - */ -public class SequenceDelimiter { - /* - * Mapping from group names to actual groups. - */ - private final Map> groups; - - /* - * The initial group to start with. - */ - private DelimiterGroup initialGroup; - - /** - * Create a new sequence delimiter. - */ - public SequenceDelimiter() { - groups = new HashMap<>(); - } - - /** - * Convert a linear sequence into a tree that matches the delimiter - * structure. - * - * Essentially, creates a parse tree of the expression against the - * following grammar while obeying the defined grouping rules. - * - *
-	 *              → ( |  | )*
-	 *          
-	 *           
-	 *
-	 *              → STRING
-	 *              → STRING
-	 *             → STRING
-	 *            → STRING
-	 * 
- * - * @param chars - * The parameters on how to mark certain portions of the - * tree. - * @param seq - * The sequence to delimit. - * - * @return The sequence as a tree that matches its group structure. Each - * node in the tree is either a data node, a subgroup node, or a - * group node. - * - * A data node is a leaf node whose data is the string it - * represents. - * - * A subgroup node is a node with two children, and the name of - * the sub-group as its label. The first child is the contents - * of the sub-group, and the second is the marker that started - * the subgroup. The marker is a leaf node labeled with its - * contents, and the contents contains a recursive tree. - * - * A group node is a node with three children, and the name of - * the group as its label. The first child is the opening - * delimiter, the second is the group contents, and the third is - * the closing delimiter. The delimiters are leaf nodes labeled - * with their contents, while the group node contains a - * recursive tree. - * - * @throws DelimiterException - * Thrown if something went wrong during sequence - * delimitation. - * - */ - public ITree delimitSequence(final SequenceCharacteristics chars, - @SuppressWarnings("unchecked") final T... seq) throws DelimiterException { - if (initialGroup == null) throw new NullPointerException("Initial group must be specified."); - else if (chars == null) throw new NullPointerException("Sequence characteristics must not be null"); - - /* - * The stack of opened and not yet closed groups. - */ - final Stack.OpenGroup> groupStack = new SimpleStack<>(); - - /* - * Open initial group. - */ - groupStack.push(initialGroup.open(chars.root, null)); - - /* - * Groups that aren't allowed to be opened at the moment. - */ - final Stack> forbiddenDelimiters = new SimpleStack<>(); - forbiddenDelimiters.push(HashMultiset.create()); - - /* - * Groups that are allowed to be opened at the moment. - */ - final Stack> allowedDelimiters = new SimpleStack<>(); - allowedDelimiters.push(HashMultimap.create()); - - /* - * Map of who forbid what for debugging purposes. - */ - final IMap whoForbid = new PushdownMap<>(); - - /* - * Process each member of the sequence. - */ - for (int i = 0; i < seq.length; i++) { - final T tok = seq[i]; - - /* - * Check if this token could open a group. - */ - final IPair possibleOpenPar = groupStack.top().doesOpen(tok); - T possibleOpen = possibleOpenPar.getLeft(); - - if (possibleOpen == null) { - /* - * Handle nested openers. - * - * Local openers take priority over nested ones - * if they overlap. - */ - if (allowedDelimiters.top().containsKey(tok)) { - possibleOpen = allowedDelimiters.top().get(tok).iterator().next(); - } - } - - /* - * If we have an opening delimiter, handle it. - */ - if (possibleOpen != null) { - final DelimiterGroup group = groups.get(possibleOpen); - - /* - * Error on groups that can't open in this - * context. - * - * This means groups that can't occur at the - * top-level of this group, as well as nested - * exclusions from all enclosing groups. - */ - if (isForbidden(groupStack, forbiddenDelimiters, possibleOpen)) { - T forbiddenBy; - - if (whoForbid.containsKey(tok)) { - forbiddenBy = whoForbid.get(tok); - } else { - forbiddenBy = groupStack.top().getName(); - } - - final String ctxList = StringUtils.toEnglishList(groupStack.toArray(), "then"); - - final String fmt = "Group '%s' can't be opened in this context. (forbidden by '%s')\nContext Stack: %s"; - - throw new DelimiterException(String.format(fmt, group, forbiddenBy, ctxList)); - } - - /* - * Add an open group. - */ - final DelimiterGroup.OpenGroup open = group.open(tok, possibleOpenPar.getRight()); - groupStack.push(open); - - /* - * Handle 'forgetful' groups that reset nesting - */ - if (open.isForgetful()) { - allowedDelimiters.push(HashMultimap.create()); - forbiddenDelimiters.push(HashMultiset.create()); - } - - /* - * Add the nested opens from this group. - */ - final Multimap currentAllowed = allowedDelimiters.top(); - for (final Entry opener : open.getNestingOpeners().entrySet()) { - currentAllowed.put(opener.getKey(), opener.getValue()); - } - - /* - * Add the nested exclusions from this group - */ - final Multiset currentForbidden = forbiddenDelimiters.top(); - for (final T exclusion : open.getNestingExclusions()) { - currentForbidden.add(exclusion); - - whoForbid.put(exclusion, possibleOpen); - } - } else if (!groupStack.empty() && groupStack.top().isClosing(tok)) { - /* - * Close the group. - */ - final DelimiterGroup.OpenGroup closed = groupStack.pop(); - - groupStack.top().addItem(closed.toTree(tok, chars)); - - /* - * Remove nested exclusions from this group. - */ - final Multiset currentForbidden = forbiddenDelimiters.top(); - for (final T excludedGroup : closed.getNestingExclusions()) { - currentForbidden.remove(excludedGroup); - - whoForbid.remove(excludedGroup); - } - - /* - * Remove the nested opens from this group. - */ - final Multimap currentAllowed = allowedDelimiters.top(); - for (final Entry closer : closed.getNestingOpeners().entrySet()) { - currentAllowed.remove(closer.getKey(), closer.getValue()); - } - - /* - * Handle 'forgetful' groups that reset nesting. - */ - if (closed.isForgetful()) { - allowedDelimiters.drop(); - forbiddenDelimiters.drop(); - } - } else if (!groupStack.empty() && groupStack.top().marksSubgroup(tok)) { - /* - * Mark a subgroup. - */ - groupStack.top().markSubgroup(tok, chars); - } else { - /* - * Add an item to the group. - */ - groupStack.top().addItem(new Tree<>(tok)); - } - } - - /* - * Error if not all groups were closed. - */ - if (groupStack.size() > 1) { - final DelimiterGroup.OpenGroup group = groupStack.top(); - - final StringBuilder msgBuilder = new StringBuilder(); - - final String closingDelims = StringUtils.toEnglishList(group.getNestingExclusions().toArray(), - false); - - final String ctxList = StringUtils.toEnglishList(groupStack.toArray(), "then"); - - msgBuilder.append("Unclosed group '"); - msgBuilder.append(group.getName()); - msgBuilder.append("'. Expected one of "); - msgBuilder.append(closingDelims); - msgBuilder.append(" to close it\nOpen groups: "); - msgBuilder.append(ctxList); - - final String fmt = "Unclosed group '%s'. Expected one of %s to close it.\nOpen groups: %n"; - - throw new DelimiterException(String.format(fmt, group.getName(), closingDelims, ctxList)); - } - - return groupStack.pop().toTree(chars.root, chars); - } - - /* - * Check if a group is forbidden to open in a context. - */ - private boolean isForbidden(final Stack.OpenGroup> groupStack, - final Stack> forbiddenDelimiters, final T groupName) { - boolean localForbid; - - /* - * Check if a delimiter is locally forbidden. - */ - if (groupStack.empty()) { - localForbid = false; - } else { - localForbid = groupStack.top().excludes(groupName); - } - - return localForbid || forbiddenDelimiters.top().contains(groupName); - } - - /** - * Add a delimiter group. - * - * @param group - * The delimiter group. - */ - public void addGroup(final DelimiterGroup group) { - if (group == null) throw new NullPointerException("Group must not be null"); - - groups.put(group.groupName, group); - } - - /** - * Creates and adds a delimiter group using the provided settings. - * - * @param openers - * The tokens that open this group - * @param groupName - * The name of the group - * @param closers - * The tokens that close this group - */ - public void addGroup(final T[] openers, final T groupName, @SuppressWarnings("unchecked") final T... closers) { - final DelimiterGroup group = new DelimiterGroup<>(groupName); - - group.addClosing(closers); - - addGroup(group); - - for (final T open : openers) { - group.addOpener(open, groupName); - } - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - - builder.append("SequenceDelimiter ["); - - if (groups != null) { - builder.append("groups="); - builder.append(groups); - builder.append(","); - } - - if (initialGroup != null) { - builder.append("initialGroup="); - builder.append(initialGroup); - } - - builder.append("]"); - - return builder.toString(); - } - - /** - * Set the initial group of this delimiter. - * - * @param initialGroup - * The initial group of this delimiter. - */ - public void setInitialGroup(final DelimiterGroup initialGroup) { - this.initialGroup = initialGroup; - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/StringDelimiter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/StringDelimiter.java deleted file mode 100644 index e3eeea5..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/delims/StringDelimiter.java +++ /dev/null @@ -1,31 +0,0 @@ -package bjc.utils.parserutils.delims; - -import bjc.utils.data.ITree; - -/** - * A sequence delimiter specialized for strings. - * - * @author EVE - * - */ -public class StringDelimiter extends SequenceDelimiter { - - /** - * Override of - * {@link SequenceDelimiter#delimitSequence(SequenceCharacteristics, Object...)} - * for ease of use for strings. - * - * @param seq - * The sequence to delimit. - * - * @return The sequence as a tree. - * - * @throws DelimiterException - * if something went wrong with delimiting the sequence. - * - * @see SequenceDelimiter - */ - public ITree delimitSequence(final String... seq) throws DelimiterException { - return super.delimitSequence(new SequenceCharacteristics<>("root", "contents", "subgroup"), seq); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ChainTokenSplitter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ChainTokenSplitter.java deleted file mode 100644 index 4736310..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ChainTokenSplitter.java +++ /dev/null @@ -1,50 +0,0 @@ -package bjc.utils.parserutils.splitter; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * A token splitter that chains several other splitters together. - * - * @author EVE - * - */ -public class ChainTokenSplitter implements TokenSplitter { - private final IList spliters; - - /** - * Create a new chain token splitter. - */ - public ChainTokenSplitter() { - spliters = new FunctionalList<>(); - } - - /** - * Append a series of splitters to the chain. - * - * @param splitters - * The splitters to append to the chain. - */ - public void appendSplitters(final TokenSplitter... splitters) { - spliters.addAll(splitters); - } - - /** - * Prepend a series of splitters to the chain. - * - * @param splitters - * The splitters to append to the chain. - */ - public void prependSplitters(final TokenSplitter... splitters) { - spliters.prependAll(splitters); - } - - @Override - public IList split(final String input) { - final IList initList = new FunctionalList<>(input); - - return spliters.reduceAux(initList, (splitter, strangs) -> { - return strangs.flatMap(splitter::split); - }); - } -} \ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ConfigurableTokenSplitter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ConfigurableTokenSplitter.java deleted file mode 100644 index 48ddcb4..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ConfigurableTokenSplitter.java +++ /dev/null @@ -1,122 +0,0 @@ -package bjc.utils.parserutils.splitter; - -import static bjc.utils.PropertyDB.applyFormat; - -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.regex.Pattern; - -import bjc.utils.funcdata.IList; - -/** - * Split a string into pieces around a regular expression, and offer an easy way - * to configure the regular expression. - * - * @author EVE - * - */ -public class ConfigurableTokenSplitter extends SimpleTokenSplitter { - private final Set simpleDelimiters; - private final Set multipleDelimiters; - private final Set rRawDelimiters; - - /** - * Create a new token splitter with blank configuration. - * - * @param keepDelims - * Whether or not to keep delimiters. - */ - public ConfigurableTokenSplitter(final boolean keepDelims) { - super(null, keepDelims); - - /* - * Use linked hash-sets to keep items in insertion order. - */ - simpleDelimiters = new LinkedHashSet<>(); - multipleDelimiters = new LinkedHashSet<>(); - rRawDelimiters = new LinkedHashSet<>(); - } - - /** - * Add a set of simple delimiters to this splitter. - * - * Simple delimiters match one occurrence of themselves as literals. - * - * @param simpleDelims - * The simple delimiters to add. - */ - public void addSimpleDelimiters(final String... simpleDelims) { - for (final String simpleDelim : simpleDelims) { - simpleDelimiters.add(simpleDelim); - } - } - - /** - * Add a set of multiple delimiters to this splitter. - * - * Multiple delimiters match one or more occurrences of themselves as - * literals. - * - * @param multiDelims - * The multiple delimiters to add. - */ - public void addMultiDelimiters(final String... multiDelims) { - for (final String multiDelim : multiDelims) { - multipleDelimiters.add(multiDelim); - } - } - - /** - * Add a set of raw delimiters to this splitter. - * - * Raw delimiters match one occurrence of themselves as regular - * expressions. - * - * @param rRawDelims - * The raw delimiters to add. - */ - public void addRawDelimiters(final String... rRawDelims) { - for (final String rRawDelim : rRawDelims) { - rRawDelimiters.add(rRawDelim); - } - } - - /** - * Take the configuration and compile it into a regular expression to - * use when splitting. - */ - public void compile() { - final StringBuilder rPattern = new StringBuilder(); - - for (final String rRawDelimiter : rRawDelimiters) { - rPattern.append(applyFormat("rawDelim", rRawDelimiter)); - } - - for (final String multipleDelimiter : multipleDelimiters) { - rPattern.append(applyFormat("multipleDelim", multipleDelimiter)); - } - - for (final String simpleDelimiter : simpleDelimiters) { - rPattern.append(applyFormat("simpleDelim", simpleDelimiter)); - } - - rPattern.deleteCharAt(rPattern.length() - 1); - - spliter = Pattern.compile(rPattern.toString()); - } - - @Override - public IList split(final String input) { - if (spliter == null) throw new IllegalStateException("Must compile splitter before use"); - - return super.split(input); - } - - @Override - public String toString() { - final String fmt = "ConfigurableTokenSplitter [simpleDelimiters=%s, multipleDelimiters=%s," - + " rRawDelimiters=%s, spliter=%s]"; - - return String.format(fmt, simpleDelimiters, multipleDelimiters, rRawDelimiters, spliter); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ExcludingTokenSplitter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ExcludingTokenSplitter.java deleted file mode 100644 index 369e7ae..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/ExcludingTokenSplitter.java +++ /dev/null @@ -1,71 +0,0 @@ -package bjc.utils.parserutils.splitter; - -import java.util.HashSet; -import java.util.Set; -import java.util.function.Predicate; - -import bjc.utils.funcdata.FunctionalList; -import bjc.utils.funcdata.IList; - -/** - * A token splitter that will not split certain tokens. - * - * @author EVE - * - */ -public class ExcludingTokenSplitter implements TokenSplitter { - private final Set literalExclusions; - - private final IList> predExclusions; - - private final TokenSplitter spliter; - - /** - * Create a new excluding token splitter. - * - * @param splitter - * The splitter to apply to non-excluded strings. - */ - public ExcludingTokenSplitter(final TokenSplitter splitter) { - spliter = splitter; - - literalExclusions = new HashSet<>(); - - predExclusions = new FunctionalList<>(); - } - - /** - * Exclude literal strings from splitting. - * - * @param exclusions - * The strings to exclude from splitting. - */ - public final void addLiteralExclusions(final String... exclusions) { - for (final String exclusion : exclusions) { - literalExclusions.add(exclusion); - } - } - - /** - * Exclude all of the strings matching any of the predicates from - * splitting. - * - * @param exclusions - * The predicates to use for exclusions. - */ - @SafeVarargs - public final void addPredicateExclusion(final Predicate... exclusions) { - for (final Predicate exclusion : exclusions) { - predExclusions.add(exclusion); - } - } - - @Override - public IList split(final String input) { - if (literalExclusions.contains(input)) - return new FunctionalList<>(input); - else if (predExclusions.anyMatch(pred -> pred.test(input))) - return new FunctionalList<>(input); - else return spliter.split(input); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/FilteredTokenSplitter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/FilteredTokenSplitter.java deleted file mode 100644 index 5d954e0..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/FilteredTokenSplitter.java +++ /dev/null @@ -1,37 +0,0 @@ -package bjc.utils.parserutils.splitter; - -import java.util.function.Predicate; - -import bjc.utils.funcdata.IList; - -/** - * A token splitter that removes tokens that match a predicate from the stream - * of tokens. - * - * @author bjculkin - * - */ -public class FilteredTokenSplitter implements TokenSplitter { - private TokenSplitter source; - - private Predicate filter; - - /** - * Create a new filtered token splitter. - * - * @param source - * The splitter to get tokens from. - * - * @param filter - * The filter to pass tokens through. - */ - public FilteredTokenSplitter(TokenSplitter source, Predicate filter) { - this.source = source; - this.filter = filter; - } - - @Override - public IList split(String input) { - return source.split(input).getMatching(filter); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java deleted file mode 100644 index c357886..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java +++ /dev/null @@ -1,46 +0,0 @@ -package bjc.utils.parserutils.splitter; - -import java.util.regex.Pattern; - -import bjc.utils.funcdata.IList; -import bjc.utils.functypes.ID; -import bjc.utils.ioutils.RegexStringEditor; - -/** - * Splits a string into pieces around a regular expression. - * - * @author EVE - * - */ -public class SimpleTokenSplitter implements TokenSplitter { - protected Pattern spliter; - - private final boolean keepDelim; - - /** - * Create a new simple token splitter. - * - * @param splitter - * The pattern to split around. - * - * @param keepDelims - * Whether or not delimiters should be kept. - */ - public SimpleTokenSplitter(final Pattern splitter, final boolean keepDelims) { - spliter = splitter; - - keepDelim = keepDelims; - } - - @Override - public IList split(final String input) { - if (keepDelim) - return RegexStringEditor.mapOccurances(input, spliter, ID.id(), ID.id()); - else return RegexStringEditor.mapOccurances(input, spliter, ID.id(), strang -> ""); - } - - @Override - public String toString() { - return String.format("SimpleTokenSplitter [spliter=%s, keepDelim=%s]", spliter, keepDelim); - } -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/TokenSplitter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/TokenSplitter.java deleted file mode 100644 index ddb28a7..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/TokenSplitter.java +++ /dev/null @@ -1,21 +0,0 @@ -package bjc.utils.parserutils.splitter; - -import bjc.utils.funcdata.IList; - -/** - * Split a string into a list of pieces. - * - * @author EVE - * - */ -public interface TokenSplitter { - /** - * Split a string into a list of pieces. - * - * @param input - * The string to split. - * - * @return The pieces of the string. - */ - public IList split(String input); -} diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/TransformTokenSplitter.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/TransformTokenSplitter.java deleted file mode 100644 index 80490f5..0000000 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/splitter/TransformTokenSplitter.java +++ /dev/null @@ -1,38 +0,0 @@ -package bjc.utils.parserutils.splitter; - -import java.util.function.UnaryOperator; - -import bjc.utils.funcdata.IList; - -/** - * A token splitter that performs a transform on the tokens from another - * splitter. - * - * @author bjculkin - * - */ -public class TransformTokenSplitter implements TokenSplitter { - private TokenSplitter source; - - private UnaryOperator transform; - - /** - * Create a new transforming splitter. - * - * @param source - * The splitter to use as a source. - * - * @param transform - * The transform to apply to tokens. - */ - public TransformTokenSplitter(TokenSplitter source, UnaryOperator transform) { - this.source = source; - this.transform = transform; - } - - @Override - public IList split(String input) { - return source.split(input).map(transform); - } - -} diff --git a/BJC-Utils2/src/test/java/bjc/utils/test/parserutils/TokenUtilsTest.java b/BJC-Utils2/src/test/java/bjc/utils/test/parserutils/TokenUtilsTest.java deleted file mode 100644 index 6fba1b2..0000000 --- a/BJC-Utils2/src/test/java/bjc/utils/test/parserutils/TokenUtilsTest.java +++ /dev/null @@ -1,152 +0,0 @@ -package bjc.utils.test.parserutils; - -import static bjc.utils.parserutils.TokenUtils.descapeString; -import static bjc.utils.parserutils.TokenUtils.removeDQuotedStrings; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.hasItems; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - -import java.util.List; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -/* - * Tests for TokenUtils - */ -public class TokenUtilsTest { - @Rule - public ExpectedException exp = ExpectedException.none(); - - /* - * Test removeDQuoted - */ - - /* - * Check handling of mismatched strings with no matching strings. - */ - @Test - public void testRemoveDQuoted_MismatchedStringNoMatch() throws IllegalArgumentException { - exp.expect(IllegalArgumentException.class); - exp.expectMessage(containsString("Opening quote was at position 0")); - - removeDQuotedStrings("\"hello"); - } - - /* - * Check handling of mismatched strings with a matching string. - */ - @Test - public void testRemoveDQuoted_MismatchedStringMatch() throws IllegalArgumentException { - exp.expect(IllegalArgumentException.class); - exp.expectMessage(containsString("Opening quote was at position 7")); - - removeDQuotedStrings("\"hello\"\""); - } - - /* - * Check handling of strings with a single embedded string. - */ - @Test - public void testRemoveDQuoted_SingleString() { - final List onSingleMatchString = removeDQuotedStrings("hello\"there\""); - - assertThat(onSingleMatchString, hasItems("hello", "\"there\"")); - } - - /* - * Check handling of strings with multiple quoted strings in a row. - */ - @Test - public void testRemoveDQuoted_MultipleSerialString() { - final List onMultipleSerialMatchString = removeDQuotedStrings("\"hello\"\"there\""); - - assertThat(onMultipleSerialMatchString, hasItems("\"hello\"", "\"there\"")); - } - - /* - * Check handling of strings with multiple interleaved strings. - */ - @Test - public void testRemoveDQuoted_MultipleInterleavedString() { - final List onMultipleInterleaveMatchString = removeDQuotedStrings("one\"two\"three\"four\""); - - assertThat(onMultipleInterleaveMatchString, hasItems("one", "\"two\"", "three", "\"four\"")); - } - - /* - * Check handling of strings without embedded strings. - */ - @Test - public void testRemoveDQuote_NoString() { - final List onNonmatchingString = removeDQuotedStrings("hello"); - - assertThat(onNonmatchingString, hasItems("hello")); - } - - /* - * Check handling of empty strings. - */ - @Test - public void testRemoveDQuote_EmptyString() { - final List onEmptyString = removeDQuotedStrings(""); - - assertThat(onEmptyString, hasItems("")); - } - - /* - * Test descapeString - */ - /* - * Check handling of empty strings. - */ - @Test - public void testDescapeString_EmptyString() { - final String onEmptyString = descapeString(""); - - assertThat(onEmptyString, is("")); - } - - /* - * Check handling of strings without escapes - */ - @Test - public void testDescapeString_NonescapeString() { - final String onNonescapeString = descapeString("hello there"); - - assertThat(onNonescapeString, is("hello there")); - } - - /* - * Check handling of strings with single escapes. - */ - @Test - public void testDescapeString_SingleEscapeString() { - final String onSingleEscapeString = descapeString("hello\\tthere"); - - assertThat(onSingleEscapeString, is("hello\tthere")); - } - - /* - * Check handling of strings with multiple escapes. - */ - @Test - public void testDescapeString_MultipleEscapeString() { - final String onMultipleEscapeString = descapeString("hello\\tthere\\tworld"); - - assertThat(onMultipleEscapeString, is("hello\tthere\tworld")); - } - - /* - * Check handling of strings with invalid single escapes. - */ - @Test - public void testDescapeString_InvalidSingleEscapeString() throws IllegalArgumentException { - exp.expect(IllegalArgumentException.class); - exp.expectMessage(containsString("at position 0")); - - descapeString("\\x"); - } -} \ No newline at end of file diff --git a/base/.amateras b/base/.amateras new file mode 100644 index 0000000..b00142f --- /dev/null +++ b/base/.amateras @@ -0,0 +1,11 @@ +#EclipseHTMLEditor configuration file +#Sat Sep 24 10:15:28 EDT 2016 +validateDTD=true +useDTD=true +validateJSP=true +validateXML=true +validateJS=true +removeMarkers=false +root=/ +validateHTML=true +javaScripts= diff --git a/base/.gitignore b/base/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/base/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/base/data/BidiMirrorDB.txt b/base/data/BidiMirrorDB.txt new file mode 100644 index 0000000..68142c5 --- /dev/null +++ b/base/data/BidiMirrorDB.txt @@ -0,0 +1,606 @@ +# BidiMirroring-9.0.0.txt +# Date: 2016-01-21, 22:00:00 GMT [KW, LI] +# © 2016 Unicode®, Inc. +# For terms of use, see http://www.unicode.org/terms_of_use.html +# +# Unicode Character Database +# For documentation, see http://www.unicode.org/reports/tr44/ +# +# Bidi_Mirroring_Glyph Property +# +# This file is an informative contributory data file in the +# Unicode Character Database. +# +# This data file lists characters that have the Bidi_Mirrored=Yes property +# value, for which there is another Unicode character that typically has a glyph +# that is the mirror image of the original character's glyph. +# +# The repertoire covered by the file is Unicode 9.0.0. +# +# The file contains a list of lines with mappings from one code point +# to another one for character-based mirroring. +# Note that for "real" mirroring, a rendering engine needs to select +# appropriate alternative glyphs, and that many Unicode characters do not +# have a mirror-image Unicode character. +# +# Each mapping line contains two fields, separated by a semicolon (';'). +# Each of the two fields contains a code point represented as a +# variable-length hexadecimal value with 4 to 6 digits. +# A comment indicates where the characters are "BEST FIT" mirroring. +# +# Code points for which Bidi_Mirrored=Yes, but for which no appropriate +# characters exist with mirrored glyphs, are +# listed as comments at the end of the file. +# +# Formally, the default value of the Bidi_Mirroring_Glyph property +# for each code point is , unless a mapping to +# some other character is specified in this data file. When a code +# point has the default value for the Bidi_Mirroring_Glyph property, +# that means that no other character exists whose glyph is suitable +# for character-based mirroring. +# +# For information on bidi mirroring, see UAX #9: Unicode Bidirectional Algorithm, +# at http://www.unicode.org/unicode/reports/tr9/ +# +# This file was originally created by Markus Scherer. +# Extended for Unicode 3.2, 4.0, 4.1, 5.0, 5.1, 5.2, and 6.0 by Ken Whistler, +# and for subsequent versions by Ken Whistler and Laurentiu Iancu. +# +# ############################################################ +# +# Property: Bidi_Mirroring_Glyph +# +# @missing: 0000..10FFFF; + +0028; 0029 # LEFT PARENTHESIS +0029; 0028 # RIGHT PARENTHESIS +003C; 003E # LESS-THAN SIGN +003E; 003C # GREATER-THAN SIGN +005B; 005D # LEFT SQUARE BRACKET +005D; 005B # RIGHT SQUARE BRACKET +007B; 007D # LEFT CURLY BRACKET +007D; 007B # RIGHT CURLY BRACKET +00AB; 00BB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +00BB; 00AB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0F3A; 0F3B # TIBETAN MARK GUG RTAGS GYON +0F3B; 0F3A # TIBETAN MARK GUG RTAGS GYAS +0F3C; 0F3D # TIBETAN MARK ANG KHANG GYON +0F3D; 0F3C # TIBETAN MARK ANG KHANG GYAS +169B; 169C # OGHAM FEATHER MARK +169C; 169B # OGHAM REVERSED FEATHER MARK +2039; 203A # SINGLE LEFT-POINTING ANGLE QUOTATION MARK +203A; 2039 # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +2045; 2046 # LEFT SQUARE BRACKET WITH QUILL +2046; 2045 # RIGHT SQUARE BRACKET WITH QUILL +207D; 207E # SUPERSCRIPT LEFT PARENTHESIS +207E; 207D # SUPERSCRIPT RIGHT PARENTHESIS +208D; 208E # SUBSCRIPT LEFT PARENTHESIS +208E; 208D # SUBSCRIPT RIGHT PARENTHESIS +2208; 220B # ELEMENT OF +2209; 220C # NOT AN ELEMENT OF +220A; 220D # SMALL ELEMENT OF +220B; 2208 # CONTAINS AS MEMBER +220C; 2209 # DOES NOT CONTAIN AS MEMBER +220D; 220A # SMALL CONTAINS AS MEMBER +2215; 29F5 # DIVISION SLASH +223C; 223D # TILDE OPERATOR +223D; 223C # REVERSED TILDE +2243; 22CD # ASYMPTOTICALLY EQUAL TO +2252; 2253 # APPROXIMATELY EQUAL TO OR THE IMAGE OF +2253; 2252 # IMAGE OF OR APPROXIMATELY EQUAL TO +2254; 2255 # COLON EQUALS +2255; 2254 # EQUALS COLON +2264; 2265 # LESS-THAN OR EQUAL TO +2265; 2264 # GREATER-THAN OR EQUAL TO +2266; 2267 # LESS-THAN OVER EQUAL TO +2267; 2266 # GREATER-THAN OVER EQUAL TO +2268; 2269 # [BEST FIT] LESS-THAN BUT NOT EQUAL TO +2269; 2268 # [BEST FIT] GREATER-THAN BUT NOT EQUAL TO +226A; 226B # MUCH LESS-THAN +226B; 226A # MUCH GREATER-THAN +226E; 226F # [BEST FIT] NOT LESS-THAN +226F; 226E # [BEST FIT] NOT GREATER-THAN +2270; 2271 # [BEST FIT] NEITHER LESS-THAN NOR EQUAL TO +2271; 2270 # [BEST FIT] NEITHER GREATER-THAN NOR EQUAL TO +2272; 2273 # [BEST FIT] LESS-THAN OR EQUIVALENT TO +2273; 2272 # [BEST FIT] GREATER-THAN OR EQUIVALENT TO +2274; 2275 # [BEST FIT] NEITHER LESS-THAN NOR EQUIVALENT TO +2275; 2274 # [BEST FIT] NEITHER GREATER-THAN NOR EQUIVALENT TO +2276; 2277 # LESS-THAN OR GREATER-THAN +2277; 2276 # GREATER-THAN OR LESS-THAN +2278; 2279 # [BEST FIT] NEITHER LESS-THAN NOR GREATER-THAN +2279; 2278 # [BEST FIT] NEITHER GREATER-THAN NOR LESS-THAN +227A; 227B # PRECEDES +227B; 227A # SUCCEEDS +227C; 227D # PRECEDES OR EQUAL TO +227D; 227C # SUCCEEDS OR EQUAL TO +227E; 227F # [BEST FIT] PRECEDES OR EQUIVALENT TO +227F; 227E # [BEST FIT] SUCCEEDS OR EQUIVALENT TO +2280; 2281 # [BEST FIT] DOES NOT PRECEDE +2281; 2280 # [BEST FIT] DOES NOT SUCCEED +2282; 2283 # SUBSET OF +2283; 2282 # SUPERSET OF +2284; 2285 # [BEST FIT] NOT A SUBSET OF +2285; 2284 # [BEST FIT] NOT A SUPERSET OF +2286; 2287 # SUBSET OF OR EQUAL TO +2287; 2286 # SUPERSET OF OR EQUAL TO +2288; 2289 # [BEST FIT] NEITHER A SUBSET OF NOR EQUAL TO +2289; 2288 # [BEST FIT] NEITHER A SUPERSET OF NOR EQUAL TO +228A; 228B # [BEST FIT] SUBSET OF WITH NOT EQUAL TO +228B; 228A # [BEST FIT] SUPERSET OF WITH NOT EQUAL TO +228F; 2290 # SQUARE IMAGE OF +2290; 228F # SQUARE ORIGINAL OF +2291; 2292 # SQUARE IMAGE OF OR EQUAL TO +2292; 2291 # SQUARE ORIGINAL OF OR EQUAL TO +2298; 29B8 # CIRCLED DIVISION SLASH +22A2; 22A3 # RIGHT TACK +22A3; 22A2 # LEFT TACK +22A6; 2ADE # ASSERTION +22A8; 2AE4 # TRUE +22A9; 2AE3 # FORCES +22AB; 2AE5 # DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE +22B0; 22B1 # PRECEDES UNDER RELATION +22B1; 22B0 # SUCCEEDS UNDER RELATION +22B2; 22B3 # NORMAL SUBGROUP OF +22B3; 22B2 # CONTAINS AS NORMAL SUBGROUP +22B4; 22B5 # NORMAL SUBGROUP OF OR EQUAL TO +22B5; 22B4 # CONTAINS AS NORMAL SUBGROUP OR EQUAL TO +22B6; 22B7 # ORIGINAL OF +22B7; 22B6 # IMAGE OF +22C9; 22CA # LEFT NORMAL FACTOR SEMIDIRECT PRODUCT +22CA; 22C9 # RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT +22CB; 22CC # LEFT SEMIDIRECT PRODUCT +22CC; 22CB # RIGHT SEMIDIRECT PRODUCT +22CD; 2243 # REVERSED TILDE EQUALS +22D0; 22D1 # DOUBLE SUBSET +22D1; 22D0 # DOUBLE SUPERSET +22D6; 22D7 # LESS-THAN WITH DOT +22D7; 22D6 # GREATER-THAN WITH DOT +22D8; 22D9 # VERY MUCH LESS-THAN +22D9; 22D8 # VERY MUCH GREATER-THAN +22DA; 22DB # LESS-THAN EQUAL TO OR GREATER-THAN +22DB; 22DA # GREATER-THAN EQUAL TO OR LESS-THAN +22DC; 22DD # EQUAL TO OR LESS-THAN +22DD; 22DC # EQUAL TO OR GREATER-THAN +22DE; 22DF # EQUAL TO OR PRECEDES +22DF; 22DE # EQUAL TO OR SUCCEEDS +22E0; 22E1 # [BEST FIT] DOES NOT PRECEDE OR EQUAL +22E1; 22E0 # [BEST FIT] DOES NOT SUCCEED OR EQUAL +22E2; 22E3 # [BEST FIT] NOT SQUARE IMAGE OF OR EQUAL TO +22E3; 22E2 # [BEST FIT] NOT SQUARE ORIGINAL OF OR EQUAL TO +22E4; 22E5 # [BEST FIT] SQUARE IMAGE OF OR NOT EQUAL TO +22E5; 22E4 # [BEST FIT] SQUARE ORIGINAL OF OR NOT EQUAL TO +22E6; 22E7 # [BEST FIT] LESS-THAN BUT NOT EQUIVALENT TO +22E7; 22E6 # [BEST FIT] GREATER-THAN BUT NOT EQUIVALENT TO +22E8; 22E9 # [BEST FIT] PRECEDES BUT NOT EQUIVALENT TO +22E9; 22E8 # [BEST FIT] SUCCEEDS BUT NOT EQUIVALENT TO +22EA; 22EB # [BEST FIT] NOT NORMAL SUBGROUP OF +22EB; 22EA # [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP +22EC; 22ED # [BEST FIT] NOT NORMAL SUBGROUP OF OR EQUAL TO +22ED; 22EC # [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL +22F0; 22F1 # UP RIGHT DIAGONAL ELLIPSIS +22F1; 22F0 # DOWN RIGHT DIAGONAL ELLIPSIS +22F2; 22FA # ELEMENT OF WITH LONG HORIZONTAL STROKE +22F3; 22FB # ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE +22F4; 22FC # SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE +22F6; 22FD # ELEMENT OF WITH OVERBAR +22F7; 22FE # SMALL ELEMENT OF WITH OVERBAR +22FA; 22F2 # CONTAINS WITH LONG HORIZONTAL STROKE +22FB; 22F3 # CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE +22FC; 22F4 # SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE +22FD; 22F6 # CONTAINS WITH OVERBAR +22FE; 22F7 # SMALL CONTAINS WITH OVERBAR +2308; 2309 # LEFT CEILING +2309; 2308 # RIGHT CEILING +230A; 230B # LEFT FLOOR +230B; 230A # RIGHT FLOOR +2329; 232A # LEFT-POINTING ANGLE BRACKET +232A; 2329 # RIGHT-POINTING ANGLE BRACKET +2768; 2769 # MEDIUM LEFT PARENTHESIS ORNAMENT +2769; 2768 # MEDIUM RIGHT PARENTHESIS ORNAMENT +276A; 276B # MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT +276B; 276A # MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT +276C; 276D # MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT +276D; 276C # MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT +276E; 276F # HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT +276F; 276E # HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT +2770; 2771 # HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT +2771; 2770 # HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT +2772; 2773 # LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT +2773; 2772 # LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT +2774; 2775 # MEDIUM LEFT CURLY BRACKET ORNAMENT +2775; 2774 # MEDIUM RIGHT CURLY BRACKET ORNAMENT +27C3; 27C4 # OPEN SUBSET +27C4; 27C3 # OPEN SUPERSET +27C5; 27C6 # LEFT S-SHAPED BAG DELIMITER +27C6; 27C5 # RIGHT S-SHAPED BAG DELIMITER +27C8; 27C9 # REVERSE SOLIDUS PRECEDING SUBSET +27C9; 27C8 # SUPERSET PRECEDING SOLIDUS +27CB; 27CD # MATHEMATICAL RISING DIAGONAL +27CD; 27CB # MATHEMATICAL FALLING DIAGONAL +27D5; 27D6 # LEFT OUTER JOIN +27D6; 27D5 # RIGHT OUTER JOIN +27DD; 27DE # LONG RIGHT TACK +27DE; 27DD # LONG LEFT TACK +27E2; 27E3 # WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK +27E3; 27E2 # WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK +27E4; 27E5 # WHITE SQUARE WITH LEFTWARDS TICK +27E5; 27E4 # WHITE SQUARE WITH RIGHTWARDS TICK +27E6; 27E7 # MATHEMATICAL LEFT WHITE SQUARE BRACKET +27E7; 27E6 # MATHEMATICAL RIGHT WHITE SQUARE BRACKET +27E8; 27E9 # MATHEMATICAL LEFT ANGLE BRACKET +27E9; 27E8 # MATHEMATICAL RIGHT ANGLE BRACKET +27EA; 27EB # MATHEMATICAL LEFT DOUBLE ANGLE BRACKET +27EB; 27EA # MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET +27EC; 27ED # MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET +27ED; 27EC # MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET +27EE; 27EF # MATHEMATICAL LEFT FLATTENED PARENTHESIS +27EF; 27EE # MATHEMATICAL RIGHT FLATTENED PARENTHESIS +2983; 2984 # LEFT WHITE CURLY BRACKET +2984; 2983 # RIGHT WHITE CURLY BRACKET +2985; 2986 # LEFT WHITE PARENTHESIS +2986; 2985 # RIGHT WHITE PARENTHESIS +2987; 2988 # Z NOTATION LEFT IMAGE BRACKET +2988; 2987 # Z NOTATION RIGHT IMAGE BRACKET +2989; 298A # Z NOTATION LEFT BINDING BRACKET +298A; 2989 # Z NOTATION RIGHT BINDING BRACKET +298B; 298C # LEFT SQUARE BRACKET WITH UNDERBAR +298C; 298B # RIGHT SQUARE BRACKET WITH UNDERBAR +298D; 2990 # LEFT SQUARE BRACKET WITH TICK IN TOP CORNER +298E; 298F # RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER +298F; 298E # LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER +2990; 298D # RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER +2991; 2992 # LEFT ANGLE BRACKET WITH DOT +2992; 2991 # RIGHT ANGLE BRACKET WITH DOT +2993; 2994 # LEFT ARC LESS-THAN BRACKET +2994; 2993 # RIGHT ARC GREATER-THAN BRACKET +2995; 2996 # DOUBLE LEFT ARC GREATER-THAN BRACKET +2996; 2995 # DOUBLE RIGHT ARC LESS-THAN BRACKET +2997; 2998 # LEFT BLACK TORTOISE SHELL BRACKET +2998; 2997 # RIGHT BLACK TORTOISE SHELL BRACKET +29B8; 2298 # CIRCLED REVERSE SOLIDUS +29C0; 29C1 # CIRCLED LESS-THAN +29C1; 29C0 # CIRCLED GREATER-THAN +29C4; 29C5 # SQUARED RISING DIAGONAL SLASH +29C5; 29C4 # SQUARED FALLING DIAGONAL SLASH +29CF; 29D0 # LEFT TRIANGLE BESIDE VERTICAL BAR +29D0; 29CF # VERTICAL BAR BESIDE RIGHT TRIANGLE +29D1; 29D2 # BOWTIE WITH LEFT HALF BLACK +29D2; 29D1 # BOWTIE WITH RIGHT HALF BLACK +29D4; 29D5 # TIMES WITH LEFT HALF BLACK +29D5; 29D4 # TIMES WITH RIGHT HALF BLACK +29D8; 29D9 # LEFT WIGGLY FENCE +29D9; 29D8 # RIGHT WIGGLY FENCE +29DA; 29DB # LEFT DOUBLE WIGGLY FENCE +29DB; 29DA # RIGHT DOUBLE WIGGLY FENCE +29F5; 2215 # REVERSE SOLIDUS OPERATOR +29F8; 29F9 # BIG SOLIDUS +29F9; 29F8 # BIG REVERSE SOLIDUS +29FC; 29FD # LEFT-POINTING CURVED ANGLE BRACKET +29FD; 29FC # RIGHT-POINTING CURVED ANGLE BRACKET +2A2B; 2A2C # MINUS SIGN WITH FALLING DOTS +2A2C; 2A2B # MINUS SIGN WITH RISING DOTS +2A2D; 2A2E # PLUS SIGN IN LEFT HALF CIRCLE +2A2E; 2A2D # PLUS SIGN IN RIGHT HALF CIRCLE +2A34; 2A35 # MULTIPLICATION SIGN IN LEFT HALF CIRCLE +2A35; 2A34 # MULTIPLICATION SIGN IN RIGHT HALF CIRCLE +2A3C; 2A3D # INTERIOR PRODUCT +2A3D; 2A3C # RIGHTHAND INTERIOR PRODUCT +2A64; 2A65 # Z NOTATION DOMAIN ANTIRESTRICTION +2A65; 2A64 # Z NOTATION RANGE ANTIRESTRICTION +2A79; 2A7A # LESS-THAN WITH CIRCLE INSIDE +2A7A; 2A79 # GREATER-THAN WITH CIRCLE INSIDE +2A7D; 2A7E # LESS-THAN OR SLANTED EQUAL TO +2A7E; 2A7D # GREATER-THAN OR SLANTED EQUAL TO +2A7F; 2A80 # LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE +2A80; 2A7F # GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE +2A81; 2A82 # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE +2A82; 2A81 # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE +2A83; 2A84 # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT +2A84; 2A83 # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT +2A8B; 2A8C # LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN +2A8C; 2A8B # GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN +2A91; 2A92 # LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL +2A92; 2A91 # GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL +2A93; 2A94 # LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL +2A94; 2A93 # GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL +2A95; 2A96 # SLANTED EQUAL TO OR LESS-THAN +2A96; 2A95 # SLANTED EQUAL TO OR GREATER-THAN +2A97; 2A98 # SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE +2A98; 2A97 # SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE +2A99; 2A9A # DOUBLE-LINE EQUAL TO OR LESS-THAN +2A9A; 2A99 # DOUBLE-LINE EQUAL TO OR GREATER-THAN +2A9B; 2A9C # DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN +2A9C; 2A9B # DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN +2AA1; 2AA2 # DOUBLE NESTED LESS-THAN +2AA2; 2AA1 # DOUBLE NESTED GREATER-THAN +2AA6; 2AA7 # LESS-THAN CLOSED BY CURVE +2AA7; 2AA6 # GREATER-THAN CLOSED BY CURVE +2AA8; 2AA9 # LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL +2AA9; 2AA8 # GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL +2AAA; 2AAB # SMALLER THAN +2AAB; 2AAA # LARGER THAN +2AAC; 2AAD # SMALLER THAN OR EQUAL TO +2AAD; 2AAC # LARGER THAN OR EQUAL TO +2AAF; 2AB0 # PRECEDES ABOVE SINGLE-LINE EQUALS SIGN +2AB0; 2AAF # SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN +2AB3; 2AB4 # PRECEDES ABOVE EQUALS SIGN +2AB4; 2AB3 # SUCCEEDS ABOVE EQUALS SIGN +2ABB; 2ABC # DOUBLE PRECEDES +2ABC; 2ABB # DOUBLE SUCCEEDS +2ABD; 2ABE # SUBSET WITH DOT +2ABE; 2ABD # SUPERSET WITH DOT +2ABF; 2AC0 # SUBSET WITH PLUS SIGN BELOW +2AC0; 2ABF # SUPERSET WITH PLUS SIGN BELOW +2AC1; 2AC2 # SUBSET WITH MULTIPLICATION SIGN BELOW +2AC2; 2AC1 # SUPERSET WITH MULTIPLICATION SIGN BELOW +2AC3; 2AC4 # SUBSET OF OR EQUAL TO WITH DOT ABOVE +2AC4; 2AC3 # SUPERSET OF OR EQUAL TO WITH DOT ABOVE +2AC5; 2AC6 # SUBSET OF ABOVE EQUALS SIGN +2AC6; 2AC5 # SUPERSET OF ABOVE EQUALS SIGN +2ACD; 2ACE # SQUARE LEFT OPEN BOX OPERATOR +2ACE; 2ACD # SQUARE RIGHT OPEN BOX OPERATOR +2ACF; 2AD0 # CLOSED SUBSET +2AD0; 2ACF # CLOSED SUPERSET +2AD1; 2AD2 # CLOSED SUBSET OR EQUAL TO +2AD2; 2AD1 # CLOSED SUPERSET OR EQUAL TO +2AD3; 2AD4 # SUBSET ABOVE SUPERSET +2AD4; 2AD3 # SUPERSET ABOVE SUBSET +2AD5; 2AD6 # SUBSET ABOVE SUBSET +2AD6; 2AD5 # SUPERSET ABOVE SUPERSET +2ADE; 22A6 # SHORT LEFT TACK +2AE3; 22A9 # DOUBLE VERTICAL BAR LEFT TURNSTILE +2AE4; 22A8 # VERTICAL BAR DOUBLE LEFT TURNSTILE +2AE5; 22AB # DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE +2AEC; 2AED # DOUBLE STROKE NOT SIGN +2AED; 2AEC # REVERSED DOUBLE STROKE NOT SIGN +2AF7; 2AF8 # TRIPLE NESTED LESS-THAN +2AF8; 2AF7 # TRIPLE NESTED GREATER-THAN +2AF9; 2AFA # DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO +2AFA; 2AF9 # DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO +2E02; 2E03 # LEFT SUBSTITUTION BRACKET +2E03; 2E02 # RIGHT SUBSTITUTION BRACKET +2E04; 2E05 # LEFT DOTTED SUBSTITUTION BRACKET +2E05; 2E04 # RIGHT DOTTED SUBSTITUTION BRACKET +2E09; 2E0A # LEFT TRANSPOSITION BRACKET +2E0A; 2E09 # RIGHT TRANSPOSITION BRACKET +2E0C; 2E0D # LEFT RAISED OMISSION BRACKET +2E0D; 2E0C # RIGHT RAISED OMISSION BRACKET +2E1C; 2E1D # LEFT LOW PARAPHRASE BRACKET +2E1D; 2E1C # RIGHT LOW PARAPHRASE BRACKET +2E20; 2E21 # LEFT VERTICAL BAR WITH QUILL +2E21; 2E20 # RIGHT VERTICAL BAR WITH QUILL +2E22; 2E23 # TOP LEFT HALF BRACKET +2E23; 2E22 # TOP RIGHT HALF BRACKET +2E24; 2E25 # BOTTOM LEFT HALF BRACKET +2E25; 2E24 # BOTTOM RIGHT HALF BRACKET +2E26; 2E27 # LEFT SIDEWAYS U BRACKET +2E27; 2E26 # RIGHT SIDEWAYS U BRACKET +2E28; 2E29 # LEFT DOUBLE PARENTHESIS +2E29; 2E28 # RIGHT DOUBLE PARENTHESIS +3008; 3009 # LEFT ANGLE BRACKET +3009; 3008 # RIGHT ANGLE BRACKET +300A; 300B # LEFT DOUBLE ANGLE BRACKET +300B; 300A # RIGHT DOUBLE ANGLE BRACKET +300C; 300D # [BEST FIT] LEFT CORNER BRACKET +300D; 300C # [BEST FIT] RIGHT CORNER BRACKET +300E; 300F # [BEST FIT] LEFT WHITE CORNER BRACKET +300F; 300E # [BEST FIT] RIGHT WHITE CORNER BRACKET +3010; 3011 # LEFT BLACK LENTICULAR BRACKET +3011; 3010 # RIGHT BLACK LENTICULAR BRACKET +3014; 3015 # LEFT TORTOISE SHELL BRACKET +3015; 3014 # RIGHT TORTOISE SHELL BRACKET +3016; 3017 # LEFT WHITE LENTICULAR BRACKET +3017; 3016 # RIGHT WHITE LENTICULAR BRACKET +3018; 3019 # LEFT WHITE TORTOISE SHELL BRACKET +3019; 3018 # RIGHT WHITE TORTOISE SHELL BRACKET +301A; 301B # LEFT WHITE SQUARE BRACKET +301B; 301A # RIGHT WHITE SQUARE BRACKET +FE59; FE5A # SMALL LEFT PARENTHESIS +FE5A; FE59 # SMALL RIGHT PARENTHESIS +FE5B; FE5C # SMALL LEFT CURLY BRACKET +FE5C; FE5B # SMALL RIGHT CURLY BRACKET +FE5D; FE5E # SMALL LEFT TORTOISE SHELL BRACKET +FE5E; FE5D # SMALL RIGHT TORTOISE SHELL BRACKET +FE64; FE65 # SMALL LESS-THAN SIGN +FE65; FE64 # SMALL GREATER-THAN SIGN +FF08; FF09 # FULLWIDTH LEFT PARENTHESIS +FF09; FF08 # FULLWIDTH RIGHT PARENTHESIS +FF1C; FF1E # FULLWIDTH LESS-THAN SIGN +FF1E; FF1C # FULLWIDTH GREATER-THAN SIGN +FF3B; FF3D # FULLWIDTH LEFT SQUARE BRACKET +FF3D; FF3B # FULLWIDTH RIGHT SQUARE BRACKET +FF5B; FF5D # FULLWIDTH LEFT CURLY BRACKET +FF5D; FF5B # FULLWIDTH RIGHT CURLY BRACKET +FF5F; FF60 # FULLWIDTH LEFT WHITE PARENTHESIS +FF60; FF5F # FULLWIDTH RIGHT WHITE PARENTHESIS +FF62; FF63 # [BEST FIT] HALFWIDTH LEFT CORNER BRACKET +FF63; FF62 # [BEST FIT] HALFWIDTH RIGHT CORNER BRACKET + +# The following characters have no appropriate mirroring character. +# For these characters it is up to the rendering system +# to provide mirrored glyphs. + +# 2140; DOUBLE-STRUCK N-ARY SUMMATION +# 2201; COMPLEMENT +# 2202; PARTIAL DIFFERENTIAL +# 2203; THERE EXISTS +# 2204; THERE DOES NOT EXIST +# 2211; N-ARY SUMMATION +# 2216; SET MINUS +# 221A; SQUARE ROOT +# 221B; CUBE ROOT +# 221C; FOURTH ROOT +# 221D; PROPORTIONAL TO +# 221F; RIGHT ANGLE +# 2220; ANGLE +# 2221; MEASURED ANGLE +# 2222; SPHERICAL ANGLE +# 2224; DOES NOT DIVIDE +# 2226; NOT PARALLEL TO +# 222B; INTEGRAL +# 222C; DOUBLE INTEGRAL +# 222D; TRIPLE INTEGRAL +# 222E; CONTOUR INTEGRAL +# 222F; SURFACE INTEGRAL +# 2230; VOLUME INTEGRAL +# 2231; CLOCKWISE INTEGRAL +# 2232; CLOCKWISE CONTOUR INTEGRAL +# 2233; ANTICLOCKWISE CONTOUR INTEGRAL +# 2239; EXCESS +# 223B; HOMOTHETIC +# 223E; INVERTED LAZY S +# 223F; SINE WAVE +# 2240; WREATH PRODUCT +# 2241; NOT TILDE +# 2242; MINUS TILDE +# 2244; NOT ASYMPTOTICALLY EQUAL TO +# 2245; APPROXIMATELY EQUAL TO +# 2246; APPROXIMATELY BUT NOT ACTUALLY EQUAL TO +# 2247; NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO +# 2248; ALMOST EQUAL TO +# 2249; NOT ALMOST EQUAL TO +# 224A; ALMOST EQUAL OR EQUAL TO +# 224B; TRIPLE TILDE +# 224C; ALL EQUAL TO +# 225F; QUESTIONED EQUAL TO +# 2260; NOT EQUAL TO +# 2262; NOT IDENTICAL TO +# 228C; MULTISET +# 22A7; MODELS +# 22AA; TRIPLE VERTICAL BAR RIGHT TURNSTILE +# 22AC; DOES NOT PROVE +# 22AD; NOT TRUE +# 22AE; DOES NOT FORCE +# 22AF; NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE +# 22B8; MULTIMAP +# 22BE; RIGHT ANGLE WITH ARC +# 22BF; RIGHT TRIANGLE +# 22F5; ELEMENT OF WITH DOT ABOVE +# 22F8; ELEMENT OF WITH UNDERBAR +# 22F9; ELEMENT OF WITH TWO HORIZONTAL STROKES +# 22FF; Z NOTATION BAG MEMBERSHIP +# 2320; TOP HALF INTEGRAL +# 2321; BOTTOM HALF INTEGRAL +# 27C0; THREE DIMENSIONAL ANGLE +# 27CC; LONG DIVISION +# 27D3; LOWER RIGHT CORNER WITH DOT +# 27D4; UPPER LEFT CORNER WITH DOT +# 27DC; LEFT MULTIMAP +# 299B; MEASURED ANGLE OPENING LEFT +# 299C; RIGHT ANGLE VARIANT WITH SQUARE +# 299D; MEASURED RIGHT ANGLE WITH DOT +# 299E; ANGLE WITH S INSIDE +# 299F; ACUTE ANGLE +# 29A0; SPHERICAL ANGLE OPENING LEFT +# 29A1; SPHERICAL ANGLE OPENING UP +# 29A2; TURNED ANGLE +# 29A3; REVERSED ANGLE +# 29A4; ANGLE WITH UNDERBAR +# 29A5; REVERSED ANGLE WITH UNDERBAR +# 29A6; OBLIQUE ANGLE OPENING UP +# 29A7; OBLIQUE ANGLE OPENING DOWN +# 29A8; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT +# 29A9; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT +# 29AA; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT +# 29AB; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT +# 29AC; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP +# 29AD; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP +# 29AE; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN +# 29AF; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN +# 29C2; CIRCLE WITH SMALL CIRCLE TO THE RIGHT +# 29C3; CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT +# 29C9; TWO JOINED SQUARES +# 29CE; RIGHT TRIANGLE ABOVE LEFT TRIANGLE +# 29DC; INCOMPLETE INFINITY +# 29E1; INCREASES AS +# 29E3; EQUALS SIGN AND SLANTED PARALLEL +# 29E4; EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE +# 29E5; IDENTICAL TO AND SLANTED PARALLEL +# 29E8; DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK +# 29E9; DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK +# 29F4; RULE-DELAYED +# 29F6; SOLIDUS WITH OVERBAR +# 29F7; REVERSE SOLIDUS WITH HORIZONTAL STROKE +# 2A0A; MODULO TWO SUM +# 2A0B; SUMMATION WITH INTEGRAL +# 2A0C; QUADRUPLE INTEGRAL OPERATOR +# 2A0D; FINITE PART INTEGRAL +# 2A0E; INTEGRAL WITH DOUBLE STROKE +# 2A0F; INTEGRAL AVERAGE WITH SLASH +# 2A10; CIRCULATION FUNCTION +# 2A11; ANTICLOCKWISE INTEGRATION +# 2A12; LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE +# 2A13; LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE +# 2A14; LINE INTEGRATION NOT INCLUDING THE POLE +# 2A15; INTEGRAL AROUND A POINT OPERATOR +# 2A16; QUATERNION INTEGRAL OPERATOR +# 2A17; INTEGRAL WITH LEFTWARDS ARROW WITH HOOK +# 2A18; INTEGRAL WITH TIMES SIGN +# 2A19; INTEGRAL WITH INTERSECTION +# 2A1A; INTEGRAL WITH UNION +# 2A1B; INTEGRAL WITH OVERBAR +# 2A1C; INTEGRAL WITH UNDERBAR +# 2A1E; LARGE LEFT TRIANGLE OPERATOR +# 2A1F; Z NOTATION SCHEMA COMPOSITION +# 2A20; Z NOTATION SCHEMA PIPING +# 2A21; Z NOTATION SCHEMA PROJECTION +# 2A24; PLUS SIGN WITH TILDE ABOVE +# 2A26; PLUS SIGN WITH TILDE BELOW +# 2A29; MINUS SIGN WITH COMMA ABOVE +# 2A3E; Z NOTATION RELATIONAL COMPOSITION +# 2A57; SLOPING LARGE OR +# 2A58; SLOPING LARGE AND +# 2A6A; TILDE OPERATOR WITH DOT ABOVE +# 2A6B; TILDE OPERATOR WITH RISING DOTS +# 2A6C; SIMILAR MINUS SIMILAR +# 2A6D; CONGRUENT WITH DOT ABOVE +# 2A6F; ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT +# 2A70; APPROXIMATELY EQUAL OR EQUAL TO +# 2A73; EQUALS SIGN ABOVE TILDE OPERATOR +# 2A74; DOUBLE COLON EQUAL +# 2A7B; LESS-THAN WITH QUESTION MARK ABOVE +# 2A7C; GREATER-THAN WITH QUESTION MARK ABOVE +# 2A85; LESS-THAN OR APPROXIMATE +# 2A86; GREATER-THAN OR APPROXIMATE +# 2A87; LESS-THAN AND SINGLE-LINE NOT EQUAL TO +# 2A88; GREATER-THAN AND SINGLE-LINE NOT EQUAL TO +# 2A89; LESS-THAN AND NOT APPROXIMATE +# 2A8A; GREATER-THAN AND NOT APPROXIMATE +# 2A8D; LESS-THAN ABOVE SIMILAR OR EQUAL +# 2A8E; GREATER-THAN ABOVE SIMILAR OR EQUAL +# 2A8F; LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN +# 2A90; GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN +# 2A9D; SIMILAR OR LESS-THAN +# 2A9E; SIMILAR OR GREATER-THAN +# 2A9F; SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN +# 2AA0; SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN +# 2AA3; DOUBLE NESTED LESS-THAN WITH UNDERBAR +# 2AB1; PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO +# 2AB2; SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO +# 2AB5; PRECEDES ABOVE NOT EQUAL TO +# 2AB6; SUCCEEDS ABOVE NOT EQUAL TO +# 2AB7; PRECEDES ABOVE ALMOST EQUAL TO +# 2AB8; SUCCEEDS ABOVE ALMOST EQUAL TO +# 2AB9; PRECEDES ABOVE NOT ALMOST EQUAL TO +# 2ABA; SUCCEEDS ABOVE NOT ALMOST EQUAL TO +# 2AC7; SUBSET OF ABOVE TILDE OPERATOR +# 2AC8; SUPERSET OF ABOVE TILDE OPERATOR +# 2AC9; SUBSET OF ABOVE ALMOST EQUAL TO +# 2ACA; SUPERSET OF ABOVE ALMOST EQUAL TO +# 2ACB; SUBSET OF ABOVE NOT EQUAL TO +# 2ACC; SUPERSET OF ABOVE NOT EQUAL TO +# 2ADC; FORKING +# 2AE2; VERTICAL BAR TRIPLE RIGHT TURNSTILE +# 2AE6; LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL +# 2AEE; DOES NOT DIVIDE WITH REVERSED NEGATION SLASH +# 2AF3; PARALLEL WITH TILDE OPERATOR +# 2AFB; TRIPLE SOLIDUS BINARY RELATION +# 2AFD; DOUBLE SOLIDUS OPERATOR +# 1D6DB; MATHEMATICAL BOLD PARTIAL DIFFERENTIAL +# 1D715; MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL +# 1D74F; MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL +# 1D789; MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL +# 1D7C3; MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL + +# EOF diff --git a/base/data/formats.sprop b/base/data/formats.sprop new file mode 100644 index 0000000..72f6e74 --- /dev/null +++ b/base/data/formats.sprop @@ -0,0 +1,160 @@ +# File storage for format strings + +################################################# +# Generic format strings for regular expressions. +################################################# + +## Format a regular expression for matching a delimiter separated list. +## Takes two parameters +## 1) The expression for each term +## 2) The expression for the delimiter +delimSeparatedList (?:%1$s(?:%2$s%1$s)*) + +###################################### +# CL format string regular expressions +###################################### + +## Format a regular expression for matching a potential CL format directive +## Has two parts +## 1) The optional set of prefix parameters +## 2) The optional modifier +## Captures three things +## 1) The prefix parameters +## 2) The modifiers +## 3) The directive name +## 4) The function name, if the directive was a function call. +clFormatDirective ~(?%1$s)?(?%2$s?)(?:%3$s) + +#################################################### +# Format strings for handling double-quoted strings. +#################################################### + +## Format the three types of string escapes into a valid pattern. +## The three types are: +## 1) Short escapes. +## 2) Octal escapes. +## 3) Unicode escapes. +stringEscape \\(%1$s|%2$s|%3$s) + +## Format the parts of a regex into one that matches java-style double-quoted strings. +## The parts are: +## 1) Anything that's not a possible escape sequence or quote. +## 2) A possible escape sequence. +doubleQuotes ("(%1$s|%2$s)*") + +##################################### +# Format strings for handling doubles +##################################### + +## Format a floating point exponent regex. +## The parts are: +## 1) Exponent indicator, +## 2) One or more digits. +fpExponent %1$s%2$s + +## Format a decimal number with an integer part. +## The parts are: +## 1) A series of decimal digits +## 2) An exponent. +## +## The number format is: +## 1) An integer part +## 2) An optional dot +## 3) An optional decimal part +## 4) An optional exponent +fpDecimalInteger (?:%1$s(?:\.?)(?:%1$s?)(?:%2$s)?) + +## Format a decimal number with no integer part. +## The parts are: +## 1) A series of decimal digits +## 2) An exponent. +## +## The number format is: +## 1) A dot +## 2) A decimal part +## 3) An optional exponent +fpDecimalDecimal (?:\.(?:%1$s)(?:%2$s)?) + +## Format a hexadecimal number with no decimal part. +## The parts are: +## 1) A series of hex digits +## +## The number format is: +## 1) A hex leader. +## 2) A series of hex digits. +## 3) An optional dot. +fpHexInteger (?:0[xX]%1$s(?:\.)?) + +## Format a hexadecimal number with a decimal part +## The parts are: +## 1) A series of hex digits. +## +## The number format is: +## 1) A hex leader. +## 2) A optional series of hex digits. +## 3) A dot. +## 4) A series of hex digits. +fpHexDecimal (?:0[xX]%1$s?(?:\.)%1$s) + +## Format a hexadecimal leader before a prefix. +## The parts are: +## 1) A hex number with no decimal part +## 2) A hex number with a decimal part +fpHexLeader (?:%1$s|%2$s) + +## Format a hexadecimal floating point number. +## The parts are: +## 1) A hexadecimal leader. +## 2) A series of decimal digits. +## +## The number format is: +## 1) A hexadecimal leader. +## 2) A exponent indicator. +## 3) An optional sign. +## 4) A series of decimal digits. +fpHexString (?:%1$s[pP][+-]?%2$s) + +## Format the number part of a double. +## The parts are: +## 1) A decimal double with an integer part. +## 2) A decimal double without an integer part. +## 3) A hexadecimal double. +fpNumber (?:%1$s|%2$s|%3$s) + +## Format a floating point leader. +## +## NOTE: The other parts are completed by where we're inserted. + +## Format a double +## The parts are: +## 1) A leader +## 2) A number +## +## NOTE: The parens are not mismatched. +## The other one is contributed by the leader. +fpDouble %1$s(?:%2$s[fFdD]?))[\x00-\x20]* + +######################################### +# Format strings for handling delimiters. +######################################### + +## Format a raw delimiter +## The parts are +## 1) A regular expression +## +## This matches just the provided regular expression. +rawDelim (?:%1$s)| + +## Format a repeating delimiter +## The parts are +## 1) A string. +## +## This matches one or more occurances of the provided string as a literal. +multipleDelim (?:\Q%1$s\E)+| + +## Format a simple delimiter +## The parts are +## 1) A string. +## +## This matches one occurrence of the provided string as a literal. +simpleDelim (?:\Q%1$s\E)| diff --git a/base/data/regexes.sprop b/base/data/regexes.sprop new file mode 100644 index 0000000..89c5b4f --- /dev/null +++ b/base/data/regexes.sprop @@ -0,0 +1,61 @@ +# File storage for static regular expressions. + +######################################################## +# Regular expressions for handling double-quoted strings +######################################################## + +## Match a possible single character escape +possibleStringEscape \\. + +## Match valid string escapes +shortFormStringEscape [btnfr"'\\] +octalStringEscape [0-3]?[0-7]{1,2} +unicodeStringEscape u[0-9a-fA-F]{4} + +## Match an unescaped quote in a string. +unescapedQuote (?[\S&&[^/]])|(?:/(?[\S&&[^/]]+)/)) +############################################## +# Miscellaneous validation regular expressions +############################################## +intLiteral \A[+\-]\d+\Z \ No newline at end of file diff --git a/base/docs/man5/BlockReaderCLI.5 b/base/docs/man5/BlockReaderCLI.5 new file mode 100644 index 0000000..178b6de --- /dev/null +++ b/base/docs/man5/BlockReaderCLI.5 @@ -0,0 +1,8 @@ +.TH BlockReaderCLI 5 "2017-09-10" "" "" +.SH NAME +BlockReaderCLI \- configure \fBBlockReaders\fP from a script +.SH DESCRIPTION +A small language for configuring \fBBlockReaders\fP from a script. This is a +command based language, where each command comes on its own line for +configuration. Certain commands may start a sub-mode. +.SH diff --git a/base/pom.xml b/base/pom.xml new file mode 100644 index 0000000..222b248 --- /dev/null +++ b/base/pom.xml @@ -0,0 +1,87 @@ + + 4.0.0 + + + bjc + BJCUtils-Parent + 1.0.0 + + + + + + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + + + + + + data/ + + **/*.txt + **/*.sprop + + + + + + bjc + BJC-Utils2 + 0.1.0-SNAPSHOT + jar + + BJC-Utils2 + http://maven.apache.org + + + UTF-8 + + + + + junit + junit + 4.12 + + + org.junit.contrib + junit-theories + 4.12 + + + com.pholser + junit-quickcheck-core + 0.5 + + + com.pholser + junit-quickcheck-generators + 0.5 + + + org.apache.commons + commons-lang3 + 3.4 + + + org.json + json + 20160212 + + + com.google.guava + guava + 21.0 + + + com.ibm.icu + icu4j + 58.2 + + + diff --git a/base/src/examples/java/bjc/utils/examples/AbbrevMapTest.java b/base/src/examples/java/bjc/utils/examples/AbbrevMapTest.java new file mode 100644 index 0000000..ac4ea76 --- /dev/null +++ b/base/src/examples/java/bjc/utils/examples/AbbrevMapTest.java @@ -0,0 +1,59 @@ +package bjc.utils.examples; + +import java.util.Scanner; + +import bjc.utils.esodata.AbbrevMap; +import bjc.utils.funcutils.StringUtils; + +/** + * Test for abbreviation map. + * + * @author EVE + * + */ +public class AbbrevMapTest { + /** + * Main method. + * + * @param args + * Unused CLI args. + */ + public static void main(final String[] args) { + final Scanner scn = new Scanner(System.in); + + final AbbrevMap map = new AbbrevMap(); + + System.out.print("Enter a command (blank line to quit): "); + String ln = scn.nextLine(); + + while (!ln.equals("")) { + final String[] commParts = ln.split(" "); + + switch (commParts[0]) { + case "add": + map.addWords(commParts[1]); + break; + case "remove": + map.removeWords(commParts[1]); + break; + case "recalc": + map.recalculate(); + break; + case "check": + final String list = StringUtils.toEnglishList(map.deabbrev(commParts[1]), false); + System.out.println(list); + break; + case "debug": + System.out.println(map.toString()); + break; + default: + System.out.println("Unknown command: " + ln); + } + + System.out.print("Enter a command (blank line to quit): "); + ln = scn.nextLine(); + } + + scn.close(); + } +} diff --git a/base/src/examples/java/bjc/utils/examples/BinarySearchTest.java b/base/src/examples/java/bjc/utils/examples/BinarySearchTest.java new file mode 100644 index 0000000..758af61 --- /dev/null +++ b/base/src/examples/java/bjc/utils/examples/BinarySearchTest.java @@ -0,0 +1,123 @@ +package bjc.utils.examples; + +import java.util.Scanner; + +import bjc.utils.funcdata.bst.BinarySearchTree; +import bjc.utils.funcdata.bst.TreeLinearizationMethod; + +/** + * Example showing how to use the binary search tree. + * + * @author ben + * + */ +public class BinarySearchTest { + private static void display(final BinarySearchTree tree, final Scanner input) { + System.out.print("What order would you like the tree to be printed in (m for options): "); + char command; + + while (true) { + command = input.nextLine().charAt(0); + TreeLinearizationMethod method = null; + + switch (command) { + case 'm': + System.out.println("Possible tree printing methods: "); + System.out.println("\tp: Preorder printing (print parent first, then left & right)."); + System.out.println("\ti: Inorder printing (print left first, then parent & right)."); + System.out.println("\to: Postorder printing (print left first, then right & parent)."); + break; + case 'p': + method = TreeLinearizationMethod.PREORDER; + break; + case 'i': + method = TreeLinearizationMethod.INORDER; + break; + case 'o': + method = TreeLinearizationMethod.POSTORDER; + break; + default: + System.out.println("ERROR: Unknown command."); + } + + if (method != null) { + tree.traverse(method, (element) -> { + System.out.println("Node: " + element); + return true; + }); + + return; + } + + System.out.print("What order would you like the tree to be printed in (m for options): "); + } + } + + /** + * Main method of class + * + * @param args + * Unused CLI args + */ + public static void main(final String[] args) { + final Scanner input = new Scanner(System.in); + System.out.println("Binary Tree Constructor/Searcher"); + final BinarySearchTree tree = new BinarySearchTree<>((o1, o2) -> o1 - o2); + + char command = ' '; + while (command != 'e') { + System.out.print("Enter a command (m for help): "); + command = input.nextLine().charAt(0); + + switch (command) { + case 'm': + System.out.println("Valid commands: "); + System.out.println("\tm: Display this help message."); + System.out.println("\te: Exit this program."); + System.out.println("\ta: Add a node to the binary tree."); + System.out.println("\td: Display the binary tree."); + System.out.println("\tr: Remove a node from the binary tree."); + System.out.println("\tf: Check if a given node is in the binary tree."); + System.out.println("\tt: Trim all deleted nodes from the tree."); + System.out.println("\tb: Balance the tree (also trims dead nodes)"); + break; + case 'a': + System.out.print("Enter the letter to add to the binary tree: "); + command = input.nextLine().charAt(0); + + tree.addNode(command); + break; + case 'r': + System.out.print("Enter the letter to add to the binary tree: "); + command = input.nextLine().charAt(0); + + tree.deleteNode(command); + break; + case 'd': + display(tree, input); + break; + case 'f': + System.out.print("Enter the letter to add to the binary tree: "); + command = input.nextLine().charAt(0); + + final boolean inTree = tree.isInTree(command); + if (inTree) { + System.out.printf("Node %s was found\n", command); + } else { + System.out.printf("Node %s was not found\n", command); + } + break; + case 't': + tree.trim(); + break; + case 'b': + tree.balance(); + break; + default: + System.out.println("ERROR: Unrecognized command."); + } + } + + input.close(); + } +} diff --git a/base/src/examples/java/bjc/utils/examples/DelimSplitterTest.java b/base/src/examples/java/bjc/utils/examples/DelimSplitterTest.java new file mode 100644 index 0000000..428f276 --- /dev/null +++ b/base/src/examples/java/bjc/utils/examples/DelimSplitterTest.java @@ -0,0 +1,504 @@ +package bjc.utils.examples; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Scanner; + +import bjc.utils.data.ITree; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; +import bjc.utils.funcutils.StringUtils; +import bjc.utils.parserutils.delims.DelimiterException; +import bjc.utils.parserutils.delims.DelimiterGroup; +import bjc.utils.parserutils.delims.RegexCloser; +import bjc.utils.parserutils.delims.RegexOpener; +import bjc.utils.parserutils.delims.SequenceDelimiter; +import bjc.utils.parserutils.delims.StringDelimiter; +import bjc.utils.parserutils.splitter.ConfigurableTokenSplitter; + +/** + * Test for {@link SequenceDelimiter} as well as + * {@link ConfigurableTokenSplitter} + * + * @author EVE + * + */ +public class DelimSplitterTest { + private ConfigurableTokenSplitter split; + + private StringDelimiter dlm; + + private Map mirrored; + + private Map> groups; + + boolean verbose; + + /* + * Create a new tester. + */ + private DelimSplitterTest() { + loadMirrorDB(); + + groups = new HashMap<>(); + + split = new ConfigurableTokenSplitter(true); + + dlm = new StringDelimiter(); + + verbose = true; + } + + private void loadMirrorDB() { + mirrored = new HashMap<>(); + + final InputStream stream = getClass().getResourceAsStream("/BidiMirrorDB.txt"); + + try (Scanner scn = new Scanner(stream)) { + String ln = ""; + + while (scn.hasNextLine()) { + ln = scn.nextLine(); + + if (ln.equals("")) { + continue; + } + if (ln.startsWith("#")) { + continue; + } + + final int cp1 = Integer.parseInt(ln.substring(0, 4), 16); + final int cp2 = Integer.parseInt(ln.substring(6, 10), 16); + + final char[] cpa1 = Character.toChars(cp1); + final char[] cpa2 = Character.toChars(cp2); + + final String cps1 = new String(cpa1); + final String cps2 = new String(cpa2); + + mirrored.put(cps1, cps2); + } + } + } + + /* + * Run the tester interface. + */ + private void runLoop() { + final Scanner scn = new Scanner(System.in); + + System.out.print("Enter a command (blank line to quit): "); + String inp = scn.nextLine().trim(); + System.out.println(); + + while (!inp.equals("")) { + handleCommand(inp, scn, true); + + System.out.println(); + + System.out.print("Enter a command (blank line to quit): "); + inp = scn.nextLine(); + + System.out.println(); + } + + scn.close(); + } + + /* + * Handle a input command. + */ + private void handleCommand(final String inp, final Scanner scn, final boolean isInteractive) { + if (inp.equals("")) return; + + int idx = inp.indexOf(' '); + + if (idx == -1) { + idx = inp.length(); + } + + final String command = inp.substring(0, idx); + + final String args = inp.substring(idx).trim(); + final String[] argArray = args.split(" "); + + switch (command) { + case "test": + handleTest(args, false); + break; + case "test-ws": + handleTest(args, true); + break; + case "splitter-split": + handleSplit(argArray); + break; + case "splitter-compile": + split.compile(); + if (verbose) { + System.out.println("Compiled splitter"); + } + break; + case "splitter-add": + split.addSimpleDelimiters(argArray); + if (verbose) { + System.out.println("Added delimiters " + StringUtils.toEnglishList(argArray, true)); + } + break; + case "splitter-addmulti": + split.addMultiDelimiters(argArray); + if (verbose) { + System.out.println( + "Added multi-delimiters " + StringUtils.toEnglishList(argArray, true)); + } + break; + case "splitter-addmatch": + for (final String arg : argArray) { + split.addSimpleDelimiters(arg, mirrored.get(arg)); + } + if (verbose) { + System.out.println("Added matched delimiters " + + StringUtils.toEnglishList(argArray, true)); + } + break; + case "splitter-debug": + System.out.println(split.toString()); + break; + case "splitter-reset": + split = new ConfigurableTokenSplitter(true); + if (verbose) { + System.out.println("Reset splitter"); + } + break; + + case "delims-addgroup": + for (final String arg : argArray) { + dlm.addGroup(groups.get(arg)); + } + if (verbose) { + System.out.println("Added groups " + StringUtils.toEnglishList(argArray, true)); + } + break; + case "delims-setinitial": + dlm.setInitialGroup(groups.get(argArray[0])); + if (verbose) { + System.out.println("Set initial group"); + } + break; + case "delims-debug": + System.out.println(dlm.toString()); + break; + case "delims-test": + handleDelim(args); + break; + case "delims-reset": + dlm = new StringDelimiter(); + if (verbose) { + System.out.println("Reset delimiter"); + } + break; + case "delimgroups-new": + for (final String arg : argArray) { + groups.put(arg, new DelimiterGroup<>(arg)); + } + if (verbose) { + System.out.println("Created groups " + StringUtils.toEnglishList(argArray, true)); + } + break; + case "delimgroups-edit": + for (final String arg : argArray) { + handleEditGroup(arg, scn, isInteractive); + } + break; + case "delimgroups-debug": + for (final DelimiterGroup group : groups.values()) { + System.out.println(group.toString()); + } + break; + case "delimgroups-reset": + dlm = new StringDelimiter(); + groups = new HashMap<>(); + if (verbose) { + System.out.println("Reset delimiter groups + delimiter"); + } + break; + case "load-file": + handleLoadFile(args); + break; + default: + System.out.println("Unknown command "); + } + + } + + /* + * Load script commands from a file. + */ + private void handleLoadFile(final String args) { + String pth = args; + + if (args.startsWith("\"")) { + pth = args.substring(1, args.length() - 1); + } + + try (FileInputStream fis = new FileInputStream(pth)) { + final Scanner scn = new Scanner(fis); + + while (scn.hasNextLine()) { + final String ln = scn.nextLine().trim(); + + if (ln.equals("")) { + continue; + } + if (ln.startsWith("#")) { + continue; + } + + if (verbose) { + System.out.println("\nRead command '" + ln + "' from file\n"); + } + handleCommand(ln, scn, false); + } + + scn.close(); + } catch (final FileNotFoundException fnfex) { + System.out.println("Couldn't find file '" + args + "'"); + } catch (final IOException ioex) { + System.out.println("I/O error with file '" + args + "'\nCause: " + ioex.getMessage()); + } + } + + /* + * Handle editing a group. + */ + private void handleEditGroup(final String arg, final Scanner scn, final boolean isInteractive) { + if (!groups.containsKey(arg)) { + System.out.println("No group named '" + arg + "'"); + return; + } + + final DelimiterGroup group = groups.get(arg); + + if (verbose) { + System.out.println("Editing group '" + arg + "'"); + } + if (isInteractive) { + System.out.println("Enter command (blank line to stop editing): "); + } + + String ln = scn.nextLine().trim(); + + while (!ln.equals("")) { + int idx = ln.indexOf(' '); + + if (idx == -1) { + idx = ln.length(); + } + + final String command = ln.substring(0, idx); + + final String args = ln.substring(idx).trim(); + final String[] argArray = args.split(" "); + + switch (command) { + case "add-closing": + group.addClosing(argArray); + if (verbose) { + System.out.println( + "Added closers " + StringUtils.toEnglishList(argArray, true)); + } + break; + case "add-tlexclude": + group.addTopLevelForbid(argArray); + if (verbose) { + System.out.println("Added top-level exclusions " + + StringUtils.toEnglishList(argArray, true)); + } + break; + case "add-exclude": + group.addTopLevelForbid(argArray); + if (verbose) { + System.out.println("Added nested exclusions " + + StringUtils.toEnglishList(argArray, true)); + } + break; + case "add-subgroup": + group.addSubgroup(argArray[0], Integer.parseInt(argArray[1])); + if (verbose) { + System.out.printf("Added subgroup %s with priority %s\n", argArray[0], + argArray[1]); + } + break; + case "add-implied-subgroup": + group.implySubgroup(argArray[0], argArray[1]); + if (verbose) { + System.out.printf("Made closer '%s' imply a '%s' subgroup\n", argArray[0], + argArray[1]); + } + break; + case "add-opener": + group.addOpener(argArray[0], argArray[1]); + if (verbose) { + System.out.printf("Added opener '%s' for group '%s'\n", argArray[0], + argArray[1]); + } + break; + case "add-reopener": + group.addPredOpener(new RegexOpener(argArray[0], argArray[1])); + if (verbose) { + System.out.printf("Added regex '%s' as opener for '%s'\n", argArray[1], + argArray[0]); + } + break; + case "add-recloser": + group.addPredCloser(new RegexCloser(argArray[0])); + if (verbose) { + System.out.printf("Added parameterized string '%s' as closer\n", argArray[0]); + } + break; + case "debug": + System.out.println(group.toString()); + break; + default: + System.out.println("Unknown command " + command); + } + + if (isInteractive) { + System.out.println("Enter command (blank line to stop editing): "); + } + + ln = scn.nextLine().trim(); + } + + if (verbose) { + System.out.println("Finished editing group '" + arg + "'"); + } + } + + private void handleDelim(final String args) { + try { + final ITree res = dlm.delimitSequence(args.split(" ")); + + printDelimSeq(res); + } catch (final DelimiterException dex) { + System.out.println("Expression '" + args + "' isn't properly delimited.\n\tCause: " + + dex.getMessage()); + } + } + + private void handleSplit(final String[] argArray) { + for (int i = 0; i < argArray.length; i++) { + final String arg = argArray[i]; + + final IList strangs = split.split(arg); + + System.out.printf("%d '%s' %s\n", i, arg, strangs); + } + } + + private void handleTest(final String inp, final boolean splitWS) { + IList strings; + + try { + strings = split.split(inp); + } catch (final IllegalStateException isex) { + System.out.println("Splitter must be compiled at least once before use."); + return; + } + + System.out.println("Split tokens: " + strings); + + if (splitWS) { + final List tks = new LinkedList<>(); + + for (final String strang : strings) { + tks.addAll(Arrays.asList(strang.split(" "))); + } + + strings = new FunctionalList<>(tks); + } + try { + final ITree delim = dlm.delimitSequence(strings.toArray(new String[0])); + + printDelimSeq(delim); + } catch (final DelimiterException dex) { + System.out.println("Expression isn't properly delimited."); + System.out.println("Cause: " + dex.getMessage()); + } + } + + private void printDelimSeq(final ITree delim) { + System.out.println("Delimited tokens:\n" + delim.getChild(1).toString()); + System.out.print("Delimited expr: "); + printDelimTree(delim); + System.out.println(); + System.out.println(); + + System.out.println(); + } + + private void printDelimTree(final ITree tree) { + final StringBuilder sb = new StringBuilder(); + + intPrintDelimTree(tree.getChild(1), sb); + + System.out.println(sb.toString().replaceAll("\\s+", " ")); + } + + private void intPrintDelimTree(final ITree tree, final StringBuilder sb) { + tree.doForChildren((child) -> { + intPrintDelimNode(child, sb); + }); + } + + private void intPrintDelimNode(final ITree tree, final StringBuilder sb) { + if (tree.getHead().equals("contents")) { + intPrintDelimTree(tree, sb); + return; + } + + switch (tree.getChildrenCount()) { + case 0: + sb.append(tree.getHead()); + sb.append(" "); + + break; + case 1: + intPrintDelimTree(tree.getChild(0), sb); + + break; + case 2: + intPrintDelimTree(tree.getChild(0).getChild(0), sb); + intPrintDelimNode(tree.getChild(1), sb); + + break; + case 3: + intPrintDelimNode(tree.getChild(0), sb); + + final ITree contents = tree.getChild(1); + + intPrintDelimTree(contents.getChild(0), sb); + intPrintDelimNode(tree.getChild(2), sb); + + break; + } + } + + /** + * Main method + * + * @param args + * Unused CLI args. + */ + public static void main(final String[] args) { + final DelimSplitterTest tst = new DelimSplitterTest(); + + tst.runLoop(); + } +} diff --git a/base/src/examples/java/bjc/utils/examples/ShuntTest.java b/base/src/examples/java/bjc/utils/examples/ShuntTest.java new file mode 100644 index 0000000..ed530ed --- /dev/null +++ b/base/src/examples/java/bjc/utils/examples/ShuntTest.java @@ -0,0 +1,37 @@ +package bjc.utils.examples; + +import java.util.Scanner; + +import bjc.utils.funcdata.FunctionalStringTokenizer; +import bjc.utils.funcdata.IList; +import bjc.utils.parserutils.ShuntingYard; + +/** + * Test of shunting yard + * + * @author ben + * + */ +public class ShuntTest { + /** + * Main method + * + * @param args + * Unused CLI args + */ + public static void main(final String[] args) { + final Scanner inputSource = new Scanner(System.in); + + System.out.print("Enter a expression to shunt: "); + final String line = inputSource.nextLine(); + + final ShuntingYard yard = new ShuntingYard<>(true); + + final IList preTokens = new FunctionalStringTokenizer(line).toList(strang -> strang); + final IList shuntedTokens = yard.postfix(preTokens, strang -> strang); + + System.out.println(shuntedTokens.toString()); + + inputSource.close(); + } +} diff --git a/base/src/examples/java/bjc/utils/examples/rangen/DiabloItemGen.java b/base/src/examples/java/bjc/utils/examples/rangen/DiabloItemGen.java new file mode 100644 index 0000000..250318f --- /dev/null +++ b/base/src/examples/java/bjc/utils/examples/rangen/DiabloItemGen.java @@ -0,0 +1,94 @@ +package bjc.utils.examples.rangen; + +import bjc.utils.funcdata.FunctionalStringTokenizer; +import bjc.utils.funcdata.IList; +import bjc.utils.gen.WeightedGrammar; + +/** + * Example showing how to use the weighted random number generator. + * + * @author ben + * + */ +public class DiabloItemGen { + private static WeightedGrammar rules = new WeightedGrammar<>(); + + private static void addCase(final String ruleName, final int probability, final String ruleParts) { + final IList parts = FunctionalStringTokenizer.fromString(ruleParts).toList(strang -> strang); + + rules.addCase(ruleName, probability, parts); + } + + private static void addInfixRules() { + final String rn = ""; + + addCase(rn, 60, "sword"); + addCase(rn, 50, "armor"); + addCase(rn, 40, "rune"); + addCase(rn, 30, "scroll"); + addCase(rn, 20, "potion"); + addCase(rn, 10, "helm"); + } + + private static void addItemRules() { + final String rn = ""; + + addCase(rn, 10, ""); + addCase(rn, 20, " "); + addCase(rn, 30, " "); + addCase(rn, 40, " "); + addCase(rn, 50, " "); + addCase(rn, 60, " "); + } + + private static void addPrefixRules() { + final String rn = ""; + + addCase(rn, 60, "sturdy"); + addCase(rn, 50, "fine"); + addCase(rn, 40, "strong"); + addCase(rn, 30, "azure"); + addCase(rn, 20, "crimson"); + addCase(rn, 10, "phasing"); + } + + private static void addSuffixRules() { + final String rn = ""; + + addCase(rn, 60, "of Health"); + addCase(rn, 50, "of Wealth"); + addCase(rn, 40, "of Life"); + addCase(rn, 30, "of the Jackal"); + addCase(rn, 20, "of Vitality"); + addCase(rn, 10, "of Ability"); + } + + /** + * Main Method + * + * @param args + * Unused CLI args + */ + public static void main(final String[] args) { + rules.addRule(""); + addItemRules(); + + rules.addRule(""); + addSuffixRules(); + + rules.addRule(""); + addPrefixRules(); + + rules.addRule(""); + addInfixRules(); + + for (int i = 0; i < 100; i++) { + final IList ls = rules.generateListValues("", " "); + + final StringBuilder sb = new StringBuilder(); + ls.forEach(sb::append); + + System.out.println(sb.toString().replaceAll("\\s+", " ")); + } + } +} diff --git a/base/src/examples/java/bjc/utils/examples/rangen/RandomStringExamples.java b/base/src/examples/java/bjc/utils/examples/rangen/RandomStringExamples.java new file mode 100644 index 0000000..a84f70d --- /dev/null +++ b/base/src/examples/java/bjc/utils/examples/rangen/RandomStringExamples.java @@ -0,0 +1,64 @@ +package bjc.utils.examples.rangen; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.FunctionalStringTokenizer; +import bjc.utils.funcdata.IList; +import bjc.utils.gen.RandomGrammar; + +/** + * Examples of random grammar + * + * @author ben + * + */ +public class RandomStringExamples { + private static RandomGrammar rg; + + private static void addRule(final String rule, final String... cases) { + final IList> cses = new FunctionalList<>(); + + for (final String strang : cases) { + final IList lst = FunctionalStringTokenizer.fromString(strang).toList(s -> s); + + cses.add(lst); + } + + rg.makeRule(rule, cses); + } + + /** + * Main method + * + * @param args + * Unused CLI args + */ + public static void main(final String[] args) { + rg = new RandomGrammar<>(); + + addRule("", " ", " thinks that I am ", + "I ", "You think that I am "); + + addRule("", "dancing", "eating", "sleeping"); + + addRule("", "", "life", "my computer", "my friends"); + + addRule("", "hate", "am jealous of", "love"); + + addRule("", "hates", "loves"); + + addRule("", "my sister", "my father", "my girlfriend", "the man next door"); + + addRule("", "creative", "intelligent"); + + addRule("", "", " with ", ""); + + for (int i = 0; i < 10; i++) { + final IList ls = rg.generateListValues("", " "); + + final StringBuilder sb = new StringBuilder(); + ls.forEach(sb::append); + + System.out.println(sb.toString().replaceAll("\\s+", " ")); + } + } +} diff --git a/base/src/examples/java/bjc/utils/examples/sample-ds-files/html.ds b/base/src/examples/java/bjc/utils/examples/sample-ds-files/html.ds new file mode 100644 index 0000000..103fa12 --- /dev/null +++ b/base/src/examples/java/bjc/utils/examples/sample-ds-files/html.ds @@ -0,0 +1,10 @@ +delimgroups-new tag initial + +delimgroups-edit tag + add-recloser + +delimgroups-edit initial + add-reopener tag <(\w+)> + +delims-addgroup tag initial +delims-setinitial initial \ No newline at end of file diff --git a/base/src/examples/java/bjc/utils/examples/sample-ds-files/json.ds b/base/src/examples/java/bjc/utils/examples/sample-ds-files/json.ds new file mode 100644 index 0000000..d110d95 --- /dev/null +++ b/base/src/examples/java/bjc/utils/examples/sample-ds-files/json.ds @@ -0,0 +1,23 @@ +splitter-addmatch ( { [ +splitter-add : , " +splitter-compile + +delimgroups-new braces brackets initial +delimgroups-edit braces + add-closing } + add-subgroup : 0 + add-subgroup , 1 + add-implied-subgroup } , + +delimgroups-edit brackets + add-subgroup , 0 + add-closing ] + add-implied-subgroup ] , + +delimgroups-edit initial + add-opener { braces + add-opener [ brackets + +delims-addgroup braces brackets initial + +delims-setinitial initial \ No newline at end of file diff --git a/base/src/examples/java/bjc/utils/examples/test.tree b/base/src/examples/java/bjc/utils/examples/test.tree new file mode 100644 index 0000000..795cc88 --- /dev/null +++ b/base/src/examples/java/bjc/utils/examples/test.tree @@ -0,0 +1,13 @@ +test 1 + 1 + 1 + 2 + 2 + 1 + +simp 1 + 2 + 3 + 4 + 3 + 2 \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/PropertyDB.java b/base/src/main/java/bjc/utils/PropertyDB.java new file mode 100644 index 0000000..713e1e0 --- /dev/null +++ b/base/src/main/java/bjc/utils/PropertyDB.java @@ -0,0 +1,160 @@ +package bjc.utils; + +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.regex.Pattern; + +import bjc.utils.funcutils.LambdaLock; +import bjc.utils.ioutils.SimpleProperties; + +/** + * Database for storage of properties from external files. + * + * @author EVE + * + */ +public class PropertyDB { + private static SimpleProperties regexes; + private static Map compiledRegexes; + + private static SimpleProperties formats; + + /* + * Whether or not to log during the loading. + */ + private static final boolean LOGLOAD = false; + + /* + * The lock to use to ensure a read can't happen during a reload + */ + private static LambdaLock loadLock = new LambdaLock(); + + static { + reloadProperties(); + } + + /** + * Reload all the properties from their files. + * + * NOTE: Any attempts to read from the property DB while properties are + * being loaded will block, to prevent reads from partial states. + */ + public static void reloadProperties() { + /* + * Do the load with the write lock taken. + */ + loadLock.write(() -> { + if (LOGLOAD) { + System.out.println("Reading regex properties:"); + } + + /* + * Load regexes. + */ + regexes = new SimpleProperties(); + regexes.loadFrom(PropertyDB.class.getResourceAsStream("/regexes.sprop"), false); + if (LOGLOAD) { + regexes.outputProperties(); + System.out.println(); + } + compiledRegexes = new HashMap<>(); + + if (LOGLOAD) { + System.out.println("Reading format properties:"); + } + + /* + * Load formats. + */ + formats = new SimpleProperties(); + formats.loadFrom(PropertyDB.class.getResourceAsStream("/formats.sprop"), false); + if (LOGLOAD) { + formats.outputProperties(); + System.out.println(); + } + }); + } + + /** + * Retrieve a persisted regular expression. + * + * @param key + * The name of the regular expression. + * + * @return The regular expression with that name. + */ + public static String getRegex(final String key) { + return loadLock.read(() -> { + if (!regexes.containsKey(key)) { + final String msg = String.format("No regular expression named '%s' found", key); + + throw new NoSuchElementException(msg); + } + + return regexes.get(key); + }); + } + + /** + * Retrieve a persisted regular expression, compiled into a regular + * expression. + * + * @param key + * The name of the regular expression. + * + * @return The regular expression with that name. + */ + public static Pattern getCompiledRegex(final String key) { + return loadLock.read(() -> { + if (!regexes.containsKey(key)) { + final String msg = String.format("No regular expression named '%s' found", key); + + throw new NoSuchElementException(msg); + } + + /* + * Get the regex, and cache a compiled version. + */ + return compiledRegexes.computeIfAbsent(key, strang -> { + return Pattern.compile(regexes.get(strang)); + }); + }); + } + + /** + * Retrieve a persisted format string. + * + * @param key + * The name of the format string. + * + * @return The format string with that name. + */ + public static String getFormat(final String key) { + return loadLock.read(() -> { + if (!formats.containsKey(key)) { + final String msg = String.format("No format string named '%s' found", key); + + throw new NoSuchElementException(msg); + } + + return formats.get(key); + }); + } + + /** + * Retrieve a persisted format string, and apply it to a set of + * arguments. + * + * @param key + * The name of the format string. + * + * @param objects + * The parameters to the format string. + * + * @return The format string with that name. + */ + public static String applyFormat(final String key, final Object... objects) { + return String.format(getFormat(key), objects); + } +} diff --git a/base/src/main/java/bjc/utils/cli/CLICommander.java b/base/src/main/java/bjc/utils/cli/CLICommander.java new file mode 100644 index 0000000..cccb255 --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/CLICommander.java @@ -0,0 +1,134 @@ +package bjc.utils.cli; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Scanner; + +/** + * Runs a CLI interface from the provided set of streams. + * + * @author ben + * + */ +public class CLICommander { + /* + * The streams used for input and normal/error output. + */ + private final InputStream input; + private final OutputStream output; + private final OutputStream error; + + /* + * The command mode to start execution in. + */ + private CommandMode initialMode; + + /** + * Create a new CLI interface powered by streams. + * + * @param input + * The stream to get user input from. + * @param output + * The stream to send normal output to. + * @param error + * The stream to send error output to. + */ + public CLICommander(final InputStream input, final OutputStream output, final OutputStream error) { + if (input == null) + throw new NullPointerException("Input stream must not be null"); + else if (output == null) + throw new NullPointerException("Output stream must not be null"); + else if (error == null) throw new NullPointerException("Error stream must not be null"); + + this.input = input; + this.output = output; + this.error = error; + } + + /** + * Start handling commands from the given input stream. + */ + public void runCommands() { + /* + * Setup output streams. + */ + final PrintStream normalOutput = new PrintStream(output); + final PrintStream errorOutput = new PrintStream(error); + + /* + * Set up input streams. + * + * We're suppressing the warning because we might use the input + * stream multiple times. + */ + @SuppressWarnings("resource") + final Scanner inputSource = new Scanner(input); + + /* + * The mode currently being used to handle commands. + * + * Used to preserve the initial mode. + */ + CommandMode currentMode = initialMode; + + /* + * Process commands until we're told to stop. + */ + while (currentMode != null) { + /* + * Print out the command prompt, using a custom prompt + * if one is specified. + */ + if (currentMode.isCustomPromptEnabled()) { + normalOutput.print(currentMode.getCustomPrompt()); + } else { + normalOutput.print(currentMode.getName() + ">> "); + } + + /* + * Read in a command. + */ + final String currentLine = inputSource.nextLine(); + + /* + * Handle commands we can handle. + */ + if (currentMode.canHandle(currentLine)) { + final String[] commandTokens = currentLine.split(" "); + String[] commandArgs = null; + + final int argCount = commandTokens.length; + + /* + * Parse args if they are present. + */ + if (argCount > 1) { + commandArgs = Arrays.copyOfRange(commandTokens, 1, argCount); + } + + /* + * Process command. + */ + currentMode = currentMode.process(commandTokens[0], commandArgs); + } else { + errorOutput.print("Error: Unrecognized command " + currentLine); + } + } + + normalOutput.print("Exiting now."); + } + + /** + * Set the initial command mode to use. + * + * @param initialMode + * The initial command mode to use. + */ + public void setInitialCommandMode(final CommandMode initialMode) { + if (initialMode == null) throw new NullPointerException("Initial mode must be non-zero"); + + this.initialMode = initialMode; + } +} diff --git a/base/src/main/java/bjc/utils/cli/Command.java b/base/src/main/java/bjc/utils/cli/Command.java new file mode 100644 index 0000000..02bc061 --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/Command.java @@ -0,0 +1,39 @@ +package bjc.utils.cli; + +/** + * Represents a command that can be invoked from a {@link CommandMode} + * + * @author ben + * + */ +public interface Command { + /** + * Create a command that serves as an alias to this one + * + * @return A command that serves as an alias to this one + */ + Command aliased(); + + /** + * Get the handler that executes this command + * + * @return The handler that executes this command + */ + CommandHandler getHandler(); + + /** + * Get the help entry for this command + * + * @return The help entry for this command + */ + CommandHelp getHelp(); + + /** + * Check if this command is an alias of another command + * + * @return Whether or not this command is an alias of another + */ + default boolean isAlias() { + return false; + } +} diff --git a/base/src/main/java/bjc/utils/cli/CommandHandler.java b/base/src/main/java/bjc/utils/cli/CommandHandler.java new file mode 100644 index 0000000..2548248 --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/CommandHandler.java @@ -0,0 +1,24 @@ +package bjc.utils.cli; + +import java.util.function.Function; + +/** + * A handler for a command + * + * @author ben + * + */ +@FunctionalInterface +public interface CommandHandler extends Function { + /** + * Execute this command + * + * @param args + * The arguments for this command + * @return The command mode to switch to after this command, or null to + * stop executing commands + */ + default CommandMode handle(final String[] args) { + return this.apply(args); + } +} diff --git a/base/src/main/java/bjc/utils/cli/CommandHelp.java b/base/src/main/java/bjc/utils/cli/CommandHelp.java new file mode 100644 index 0000000..86567a0 --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/CommandHelp.java @@ -0,0 +1,31 @@ +package bjc.utils.cli; + +/** + * Interface for the help entry for a command + * + * @author ben + * + */ +public interface CommandHelp { + /** + * Get the description of a command. + * + * @return The description of a command + */ + String getDescription(); + + /** + * Get the summary line for a command. + * + * A summary line should consist of a string of the following format + * + *
+	 * "<command-name>\t<command-summary>"
+	 * 
+ * + * where anything in angle brackets should be filled in. + * + * @return The summary line line for a command + */ + String getSummary(); +} diff --git a/base/src/main/java/bjc/utils/cli/CommandMode.java b/base/src/main/java/bjc/utils/cli/CommandMode.java new file mode 100644 index 0000000..39c72fc --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/CommandMode.java @@ -0,0 +1,72 @@ +package bjc.utils.cli; + +/** + * A mode for determining the commands that are valid to enter, and then + * handling those commands + * + * @author ben + * + */ +public interface CommandMode extends Comparable { + /** + * Check to see if this mode can handle the specified command + * + * @param command + * The command to check + * @return Whether or not this mode can handle the command. It is + * assumed not by default + */ + default boolean canHandle(final String command) { + return false; + }; + + /** + * Get the custom prompt for this mode + * + * @return the custom prompt for this mode + * + * @throws UnsupportedOperationException + * if this mode doesn't support a custom prompt + */ + default String getCustomPrompt() { + throw new UnsupportedOperationException("This mode doesn't support a custom prompt"); + } + + /** + * Get the name of this command mode + * + * @return The name of this command mode, which is the empty string by + * default + */ + public default String getName() { + return ""; + } + + /** + * Check if this mode uses a custom prompt + * + * @return Whether or not this mode uses a custom prompt + */ + default boolean isCustomPromptEnabled() { + return false; + } + + /** + * Process a command in this mode + * + * @param command + * The command to process + * @param args + * A list of arguments to the command + * @return The command mode to use for the next command. Defaults to + * returning this, and doing nothing else + */ + default CommandMode process(final String command, final String[] args) { + return this; + } + + @Override + default int compareTo(final CommandMode o) { + return getName().compareTo(o.getName()); + } +} diff --git a/base/src/main/java/bjc/utils/cli/DelegatingCommand.java b/base/src/main/java/bjc/utils/cli/DelegatingCommand.java new file mode 100644 index 0000000..acaa3a6 --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/DelegatingCommand.java @@ -0,0 +1,64 @@ +package bjc.utils.cli; + +/** + * A class for a command that delegates to another command. + * + * @author ben + * + */ +class DelegatingCommand implements Command { + /* + * The command to delegate to. + */ + private final Command delegate; + + /** + * Create a new command that delegates to another command. + * + * @param delegate + * The command to delegate to. + */ + public DelegatingCommand(final Command delegate) { + this.delegate = delegate; + } + + @Override + public Command aliased() { + return new DelegatingCommand(delegate); + } + + @Override + public CommandHandler getHandler() { + return delegate.getHandler(); + } + + @Override + public CommandHelp getHelp() { + return delegate.getHelp(); + } + + @Override + public boolean isAlias() { + return true; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("DelegatingCommand ["); + + if (delegate != null) { + builder.append("delegate="); + builder.append(delegate); + } + + builder.append("]"); + + return builder.toString(); + } +} diff --git a/base/src/main/java/bjc/utils/cli/GenericCommand.java b/base/src/main/java/bjc/utils/cli/GenericCommand.java new file mode 100644 index 0000000..4ae4dea --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/GenericCommand.java @@ -0,0 +1,84 @@ +package bjc.utils.cli; + +/** + * Generic command implementation. + * + * @author ben + * + */ +public class GenericCommand implements Command { + /* + * The behavior for invoking the command. + */ + private final CommandHandler handler; + + /* + * The help for the command. + */ + private CommandHelp help; + + /** + * Create a new generic command. + * + * @param handler + * The handler to use for the command. + * @param description + * The description of the command. May be null, in which + * case a default is provided. + * @param help + * The detailed help message for the command. May be + * null, in which case the description is repeated for + * the detailed help. + */ + public GenericCommand(final CommandHandler handler, final String description, final String help) { + if (handler == null) throw new NullPointerException("Command handler must not be null"); + + this.handler = handler; + + if (description == null) { + this.help = new NullHelp(); + } else { + this.help = new GenericHelp(description, help); + } + } + + @Override + public Command aliased() { + return new DelegatingCommand(this); + } + + @Override + public CommandHandler getHandler() { + return handler; + } + + @Override + public CommandHelp getHelp() { + return help; + } + + @Override + public boolean isAlias() { + return false; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("GenericCommand ["); + + if (help != null) { + builder.append("help="); + builder.append(help); + } + + builder.append("]"); + + return builder.toString(); + } +} diff --git a/base/src/main/java/bjc/utils/cli/GenericCommandMode.java b/base/src/main/java/bjc/utils/cli/GenericCommandMode.java new file mode 100644 index 0000000..8764537 --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/GenericCommandMode.java @@ -0,0 +1,469 @@ +package bjc.utils.cli; + +import java.util.TreeMap; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IMap; + +/** + * A general command mode, with a customizable set of commands + * + * There is a small set of commands which is handled by default. The first is + * 'list', which lists all the commands the user can input. The second is + * 'alias', which allows the user to bind a new name to a command + * + * @author ben + * + */ +public class GenericCommandMode implements CommandMode { + /* + * Contains the commands this mode handles + */ + private final IMap commandHandlers; + private final IMap defaultHandlers; + + /* + * Contains help topics without an associated command + */ + private final IMap helpTopics; + + /* + * The action to execute upon encountering an unknown command + */ + private BiConsumer unknownCommandHandler; + + /* + * The functions to use for input/output + */ + private final Consumer errorOutput; + private final Consumer normalOutput; + + /* + * The name of this command mode, or null if it is unnamed + */ + private String modeName; + + /* + * The custom prompt to use, or null if none is specified + */ + private String customPrompt; + + /** + * Create a new generic command mode + * + * @param normalOutput + * The function to use for normal output + * @param errorOutput + * The function to use for error output + */ + public GenericCommandMode(final Consumer normalOutput, final Consumer errorOutput) { + if (normalOutput == null) + throw new NullPointerException("Normal output source must be non-null"); + else if (errorOutput == null) throw new NullPointerException("Error output source must be non-null"); + + this.normalOutput = normalOutput; + this.errorOutput = errorOutput; + + /* + * Initialize handler maps so that they sort in alphabetical + */ + /* + * order + */ + commandHandlers = new FunctionalMap<>(new TreeMap<>()); + defaultHandlers = new FunctionalMap<>(new TreeMap<>()); + helpTopics = new FunctionalMap<>(new TreeMap<>()); + + setupDefaultCommands(); + } + + /** + * Add an alias to an existing command + * + * @param commandName + * The name of the command to add an alias for + * @param aliasName + * The new alias for the command + * + * @throws IllegalArgumentException + * if the specified command doesn't have a bound + * handler, or if the alias name already has a bound + * value + */ + public void addCommandAlias(final String commandName, final String aliasName) { + if (commandName == null) + throw new NullPointerException("Command name must not be null"); + else if (aliasName == null) + throw new NullPointerException("Alias name must not be null"); + else if (!commandHandlers.containsKey(commandName) && !defaultHandlers.containsKey(commandName)) + throw new IllegalArgumentException("Cannot alias non-existant command '" + commandName + "'"); + else if (commandHandlers.containsKey(aliasName) || defaultHandlers.containsKey(aliasName)) + throw new IllegalArgumentException( + "Cannot bind alias '" + aliasName + "' to a command with a bound handler"); + else { + Command aliasedCommand; + + if (defaultHandlers.containsKey(commandName)) { + aliasedCommand = defaultHandlers.get(commandName).aliased(); + } else { + aliasedCommand = commandHandlers.get(commandName).aliased(); + } + + commandHandlers.put(aliasName, aliasedCommand); + } + } + + /** + * Add a command to this command mode + * + * @param command + * The name of the command to add + * @param handler + * The handler to use for the specified command + * + * @throws IllegalArgumentException + * if the specified command already has a handler + * registered + */ + public void addCommandHandler(final String command, final Command handler) { + if (command == null) + throw new NullPointerException("Command must not be null"); + else if (handler == null) + throw new NullPointerException("Handler must not be null"); + else if (canHandle(command)) + throw new IllegalArgumentException("Command " + command + " already has a handler registered"); + else { + commandHandlers.put(command, handler); + } + } + + /** + * Add a help topic to this command mode that isn't tied to a command + * + * @param topicName + * The name of the topic + * @param topic + * The contents of the topic + */ + public void addHelpTopic(final String topicName, final CommandHelp topic) { + helpTopics.put(topicName, topic); + } + + /* + * Default command builders + */ + + private GenericCommand buildAliasCommand() { + final String aliasShortHelp = "alias\tAlias one command to another"; + final String aliasLongHelp = "Gives a command another name it can be invoked by." + + " Invoke with two arguments: the name of the command to alias" + + "followed by the name of the alias to give that command."; + + return new GenericCommand((args) -> { + doAliasCommands(args); + + return this; + }, aliasShortHelp, aliasLongHelp); + } + + private GenericCommand buildClearCommands() { + final String clearShortHelp = "clear\tClear the screen"; + final String clearLongHelp = "Clears the screen of all the text on it," + " and prints a new prompt."; + + return new GenericCommand((args) -> { + errorOutput.accept("ERROR: This console doesn't support screen clearing"); + + return this; + }, clearShortHelp, clearLongHelp); + } + + private GenericCommand buildExitCommand() { + final String exitShortHelp = "exit\tExit the console"; + final String exitLongHelp = "First prompts the user to make sure they want to" + + " exit, then quits if they say they do"; + + return new GenericCommand((args) -> { + errorOutput.accept("ERROR: This console doesn't support auto-exiting"); + + return this; + }, exitShortHelp, exitLongHelp); + } + + private GenericCommand buildHelpCommand() { + final String helpShortHelp = "help\tConsult the help system"; + final String helpLongHelp = "Consults the internal help system." + + " Invoked in two different ways. Invoking with no arguments" + + " causes all the topics you can ask for details on to be list," + + " while invoking with the name of a topic will print the entry" + " for that topic"; + + return new GenericCommand((args) -> { + if (args == null || args.length == 0) { + /* + * Invoke general help + */ + doHelpSummary(); + } else { + /* + * Invoke help for a command + */ + doHelpCommand(args[0]); + } + + return this; + }, helpShortHelp, helpLongHelp); + } + + private GenericCommand buildListCommand() { + final String listShortHelp = "list\tList available commands"; + final String listLongHelp = "Lists all of the commands available in this mode," + + " as well as commands available in any mode"; + + return new GenericCommand((args) -> { + doListCommands(); + + return this; + }, listShortHelp, listLongHelp); + } + + @Override + public boolean canHandle(final String command) { + return commandHandlers.containsKey(command) || defaultHandlers.containsKey(command); + } + + /* + * Implement default commands + */ + + private void doAliasCommands(final String[] args) { + if (args.length != 2) { + errorOutput.accept("ERROR: Alias requires two arguments." + + " The command name, and the alias for that command"); + } else { + final String commandName = args[0]; + final String aliasName = args[1]; + + if (!canHandle(commandName)) { + errorOutput.accept("ERROR: '" + commandName + "' is not a valid command."); + } else if (canHandle(aliasName)) { + errorOutput.accept("ERROR: Cannot overwrite command '" + aliasName + "'"); + } else { + addCommandAlias(commandName, aliasName); + } + } + } + + private void doHelpCommand(final String commandName) { + if (commandHandlers.containsKey(commandName)) { + final String desc = commandHandlers.get(commandName).getHelp().getDescription(); + + normalOutput.accept("\n" + desc); + } else if (defaultHandlers.containsKey(commandName)) { + final String desc = defaultHandlers.get(commandName).getHelp().getDescription(); + + normalOutput.accept("\n" + desc); + } else if (helpTopics.containsKey(commandName)) { + normalOutput.accept("\n" + helpTopics.get(commandName).getDescription()); + } else { + errorOutput.accept( + "ERROR: I'm sorry, but there is no help available for '" + commandName + "'"); + } + } + + private void doHelpSummary() { + normalOutput.accept("Help topics for this command mode are as follows:\n"); + + if (commandHandlers.size() > 0) { + commandHandlers.forEachValue(command -> { + if (!command.isAlias()) { + normalOutput.accept("\t" + command.getHelp().getSummary() + "\n"); + } + }); + } else { + normalOutput.accept("\tNone available\n"); + } + + normalOutput.accept("\nHelp topics available in all command modes are as follows\n"); + if (defaultHandlers.size() > 0) { + defaultHandlers.forEachValue(command -> { + if (!command.isAlias()) { + normalOutput.accept("\t" + command.getHelp().getSummary() + "\n"); + } + }); + } else { + normalOutput.accept("\tNone available\n"); + } + + normalOutput.accept("\nHelp topics not associated with a command are as follows\n"); + if (helpTopics.size() > 0) { + helpTopics.forEachValue(topic -> { + normalOutput.accept("\t" + topic.getSummary() + "\n"); + }); + } else { + normalOutput.accept("\tNone available\n"); + } + } + + private void doListCommands() { + normalOutput.accept("The available commands for this mode are as follows:\n"); + + commandHandlers.keyList().forEach(commandName -> { + normalOutput.accept("\t" + commandName); + }); + + normalOutput.accept("\nThe following commands are available in all modes:\n"); + defaultHandlers.keyList().forEach(commandName -> { + normalOutput.accept("\t" + commandName); + }); + + normalOutput.accept("\n"); + } + + @Override + public String getCustomPrompt() { + if (customPrompt != null) return customPrompt; + + return CommandMode.super.getCustomPrompt(); + } + + @Override + public String getName() { + if (modeName != null) return modeName; + + return CommandMode.super.getName(); + } + + @Override + public boolean isCustomPromptEnabled() { + return customPrompt != null; + } + + @Override + public CommandMode process(final String command, final String[] args) { + normalOutput.accept("\n"); + + if (defaultHandlers.containsKey(command)) + return defaultHandlers.get(command).getHandler().handle(args); + else if (commandHandlers.containsKey(command)) + return commandHandlers.get(command).getHandler().handle(args); + else { + if (args != null) { + errorOutput.accept("ERROR: Unrecognized command " + command + String.join(" ", args)); + } else { + errorOutput.accept("ERROR: Unrecognized command " + command); + } + + if (unknownCommandHandler == null) + throw new UnsupportedOperationException("Command " + command + " is invalid."); + + unknownCommandHandler.accept(command, args); + } + + return this; + } + + /** + * Set the custom prompt for this mode + * + * @param prompt + * The custom prompt for this mode, or null to disable + * the custom prompt + */ + public void setCustomPrompt(final String prompt) { + customPrompt = prompt; + } + + /** + * Set the name of this mode + * + * @param name + * The desired name of this mode, or null to use the + * default name + */ + public void setModeName(final String name) { + modeName = name; + } + + /** + * Set the handler to use for unknown commands + * + * @param handler + * The handler to use for unknown commands, or null to + * throw on unknown commands + */ + public void setUnknownCommandHandler(final BiConsumer handler) { + if (handler == null) throw new NullPointerException("Handler must not be null"); + + unknownCommandHandler = handler; + } + + private void setupDefaultCommands() { + defaultHandlers.put("list", buildListCommand()); + defaultHandlers.put("alias", buildAliasCommand()); + defaultHandlers.put("help", buildHelpCommand()); + + addCommandAlias("help", "man"); + + /* + * Add commands handled in a upper layer. + */ + + /* + * @TODO figure out a place to put commands that apply across + */ + /* + * all + */ + /* + * modes, but only apply to a specific application + */ + defaultHandlers.put("clear", buildClearCommands()); + defaultHandlers.put("exit", buildExitCommand()); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("GenericCommandMode ["); + + if (commandHandlers != null) { + builder.append("commandHandlers="); + builder.append(commandHandlers); + } + + if (defaultHandlers != null) { + builder.append(", "); + builder.append("defaultHandlers="); + builder.append(defaultHandlers); + } + + if (helpTopics != null) { + builder.append(", "); + builder.append("helpTopics="); + builder.append(helpTopics); + } + + if (modeName != null) { + builder.append(", "); + builder.append("modeName="); + builder.append(modeName); + } + + if (customPrompt != null) { + builder.append(", "); + builder.append("customPrompt="); + builder.append(customPrompt); + } + + builder.append("]"); + + return builder.toString(); + } + +} diff --git a/base/src/main/java/bjc/utils/cli/GenericHelp.java b/base/src/main/java/bjc/utils/cli/GenericHelp.java new file mode 100644 index 0000000..38adf57 --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/GenericHelp.java @@ -0,0 +1,63 @@ +package bjc.utils.cli; + +/** + * Generic implementation of a help topic + * + * @author ben + * + */ +public class GenericHelp implements CommandHelp { + // The strings for this help topic + private final String summary; + private final String description; + + /** + * Create a new help topic + * + * @param summary + * The summary of this help topic + * @param description + * The description of this help topic, or null if this + * help topic doesn't have a more detailed description + */ + public GenericHelp(final String summary, final String description) { + if (summary == null) throw new NullPointerException("Help summary must be non-null"); + + this.summary = summary; + this.description = description; + } + + @Override + public String getDescription() { + if (description == null) return summary; + + return description; + } + + @Override + public String getSummary() { + return summary; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + + builder.append("GenericHelp ["); + + if (summary != null) { + builder.append("summary="); + builder.append(summary); + } + + if (description != null) { + builder.append(", "); + builder.append("description="); + builder.append(description); + } + + builder.append("]"); + + return builder.toString(); + } +} diff --git a/base/src/main/java/bjc/utils/cli/NullHelp.java b/base/src/main/java/bjc/utils/cli/NullHelp.java new file mode 100644 index 0000000..6c49ae6 --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/NullHelp.java @@ -0,0 +1,20 @@ +package bjc.utils.cli; + +/** + * Implementation of a help topic that doesn't exist + * + * @author ben + * + */ +public class NullHelp implements CommandHelp { + @Override + public String getDescription() { + return "No description provided"; + } + + @Override + public String getSummary() { + return "No summary provided"; + } + +} diff --git a/base/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java b/base/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java new file mode 100644 index 0000000..ec66fe2 --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java @@ -0,0 +1,392 @@ +package bjc.utils.cli.objects; + +import java.io.InputStreamReader; +import java.io.Reader; + +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.function.Predicate; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import bjc.utils.ioutils.Prompter; +import bjc.utils.ioutils.blocks.*; + +import static bjc.utils.cli.objects.Command.CommandStatus; +import static bjc.utils.cli.objects.Command.CommandStatus.*; + +public class BlockReaderCLI { + private final Logger LOGGER = Logger.getLogger(BlockReaderCLI.class.getName()); + + public static class BlockReaderState { + public final Map readers; + public final Map sources; + + public BlockReaderState(Map readers, Map sources) { + this.readers = readers; + this.sources = sources; + } + } + + private BlockReaderState stat; + + /** + * Create a new CLI for configuring BlockReaders. + * + * @param srcs + * The container of initial I/O sources. + */ + public BlockReaderCLI(Map srcs) { + stat = new BlockReaderState(new HashMap<>(), srcs); + } + + public static void main(String[] args) { + /* + * Create/configure I/O sources. + */ + Map sources = new HashMap<>(); + sources.put("stdio", new InputStreamReader(System.in)); + + BlockReaderCLI reader = new BlockReaderCLI(sources); + + reader.run(new Scanner(System.in), "console", true); + } + + /** + * Run the CLI on an input source. + * + * @param input + * The place to read input from. + * @param ioSource + * The name of the place to read input from. + * @param interactive + * Whether or not the source is interactive + */ + public void run(Scanner input, String ioSource, boolean interactive) { + int lno = 0; + while(input.hasNextLine()) { + if(interactive) + System.out.printf("reader-conf(%d)>", lno); + + String ln = input.nextLine(); + + lno += 1; + + Command com = Command.fromString(ln, lno, ioSource); + if(com == null) continue; + + CommandStatus stat = handleCommand(com, interactive); + if(stat == FINISH || stat == ERROR) { + return; + } + } + + input.close(); + } + + /* + * Handle a command. + */ + public CommandStatus handleCommand(Command com, boolean interactive) { + switch(com.nameCommand) { + case "def-filtered": + return defFiltered(com); + case "def-layered": + return defLayered(com); + case "def-pushback": + return defPushback(com); + case "def-simple": + return defSimple(com); + case "def-serial": + return defSerial(com); + case "def-toggled": + return defToggled(com); + case "}": + case "end": + case "exit": + case "quit": + if(interactive) + System.out.printf("Exiting reader-conf, %d readers configured in %d commands\n", + stat.readers.size(), com.lineNo); + return FINISH; + default: + LOGGER.severe(com.error("Unknown command '%s'\n", com.nameCommand)); + return FAIL; + } + } + + private CommandStatus defFiltered(Command com) { + String remn = com.remnCommand; + + /* + * Get the block name. + */ + int idx = remn.indexOf(' '); + if(idx == -1) { + LOGGER.severe(com.error("No name argument for def-filtered.\n")); + return FAIL; + } + String blockName = remn.substring(0, idx).trim(); + remn = remn.substring(idx).trim(); + + /* + * Check there isn't a reader already bound to this name. + */ + if(stat.readers.containsKey(blockName)) { + LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName)); + } + + /* + * Get the reader name. + */ + idx = remn.indexOf(' '); + if(idx == -1) { + LOGGER.severe(com.error("No reader-name argument for def-filtered.\n")); + return FAIL; + } + String readerName = remn.substring(0, idx).trim(); + remn = remn.substring(idx).trim(); + + /* + * Check there is a reader bound to that name. + */ + if(!stat.readers.containsKey(readerName)) { + LOGGER.severe(com.error("No source named %s\n", readerName)); + return FAIL; + } + + /* + * Get the pattern. + */ + if(remn.equals("")) { + LOGGER.severe(com.error("No filter argument for def-filtered\n")); + return FAIL; + } + + String filter = remn; + + try { + Pattern pat = Pattern.compile(filter); + + Predicate pred = (block) -> { + Matcher mat = pat.matcher(block.contents); + + return mat.matches(); + }; + + BlockReader reader = new FilteredBlockReader(stat.readers.get(readerName), pred); + + stat.readers.put(blockName, reader); + } catch (PatternSyntaxException psex) { + LOGGER.severe(com.error("Invalid regular expression '%s' for filter. (%s)\n", filter, psex.getMessage())); + return FAIL; + } + + return SUCCESS; + } + + private CommandStatus defPushback(Command com) { + String[] parts = com.remnCommand.split(" "); + + if(parts.length != 2) { + LOGGER.severe(com.error("Incorrect number of arguments to def-pushback. Requires a block name and a reader name\n")); + return FAIL; + } + + String blockName = parts[0]; + if(stat.readers.containsKey(blockName)) { + LOGGER.warning(com.warn("Shadowing existing reader %s\n", blockName)); + return FAIL; + } + + String readerName = parts[1]; + if(!stat.readers.containsKey(readerName)) { + LOGGER.severe(com.error("No reader named %s\n", readerName)); + return FAIL; + } + + BlockReader reader = new PushbackBlockReader(stat.readers.get(readerName)); + stat.readers.put(blockName, reader); + + return SUCCESS; + } + + private CommandStatus defToggled(Command com) { + String[] parts = com.remnCommand.split(" "); + + if(parts.length != 3) { + LOGGER.severe(com.error("Incorrect number of arguments to def-toggled. Requires a block name and two reader names\n")); + return FAIL; + } + + /* + * Get the block name. + */ + String blockName = parts[0]; + if(stat.readers.containsKey(blockName)) { + LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName)); + } + + /* + * Make sure the component readers exist. + */ + if(!stat.readers.containsKey(parts[1])) { + LOGGER.severe(com.error("No reader named %s\n", parts[1])); + return FAIL; + } + + if(!stat.readers.containsKey(parts[2])) { + LOGGER.severe(com.error("No reader named %s\n", parts[2])); + return FAIL; + } + + BlockReader reader = new ToggledBlockReader(stat.readers.get(parts[1]), stat.readers.get(parts[2])); + stat.readers.put(blockName, reader); + + return SUCCESS; + } + + private CommandStatus defLayered(Command com) { + String[] parts = com.remnCommand.split(" "); + + if(parts.length != 3) { + LOGGER.severe(com.error("Incorrect number of arguments to def-layered. Requires a block name and two reader names\n")); + return FAIL; + } + + /* + * Get the block name. + */ + String blockName = parts[0]; + if(stat.readers.containsKey(blockName)) { + LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName)); + } + + /* + * Make sure the component readers exist. + */ + if(!stat.readers.containsKey(parts[1])) { + LOGGER.severe(com.error("No reader named %s\n", parts[1])); + return FAIL; + } + + if(!stat.readers.containsKey(parts[2])) { + LOGGER.severe(com.error("No reader named %s\n", parts[2])); + return FAIL; + } + + BlockReader reader = new LayeredBlockReader(stat.readers.get(parts[1]), stat.readers.get(parts[2])); + stat.readers.put(blockName, reader); + + return SUCCESS; + } + + private CommandStatus defSerial(Command com) { + String[] parts = com.remnCommand.split(" "); + + if(parts.length < 2) { + LOGGER.severe(com.error("Not enough arguments to def-serial. Requires at least a block name and at least one reader name\n")); + return FAIL; + } + + /* + * Get the name for this BlockReader. + */ + String blockName = parts[0]; + /* + * Check there isn't a reader already bound to this name. + */ + if(stat.readers.containsKey(blockName)) { + LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName)); + } + + /* + * Get all of the component readers. + */ + BlockReader[] readerArr = new BlockReader[parts.length - 1]; + for(int i = 1; i < parts.length; i++) { + String readerName = parts[i]; + + /* + * Check there is a reader bound to that name. + */ + if(!stat.readers.containsKey(readerName)) { + LOGGER.severe(com.error("No reader named %s\n", readerName)); + return FAIL; + } + + readerArr[i] = stat.readers.get(readerName); + } + + BlockReader reader = new SerialBlockReader(readerArr); + + stat.readers.put(blockName, reader); + + return SUCCESS; + } + + private CommandStatus defSimple(Command com) { + String remn = com.remnCommand; + + /* + * Get the block name. + */ + int idx = remn.indexOf(' '); + if(idx == -1) { + LOGGER.severe(com.error("No name argument for def-simple.\n")); + return FAIL; + } + String blockName = remn.substring(0, idx).trim(); + remn = remn.substring(idx).trim(); + + /* + * Check there isn't a reader already bound to this name. + */ + if(stat.readers.containsKey(blockName)) { + LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName)); + } + + /* + * Get the source name. + */ + idx = remn.indexOf(' '); + if(idx == -1) { + LOGGER.severe(com.error("No source-name argument for def-simple.\n")); + return FAIL; + } + String sourceName = remn.substring(0, idx).trim(); + remn = remn.substring(idx).trim(); + + /* + * Check there is a source bound to that name. + */ + if(!stat.sources.containsKey(sourceName)) { + LOGGER.severe(com.error("No source named %s\n", sourceName)); + return FAIL; + } + + /* + * Get the pattern. + */ + if(remn.equals("")) { + LOGGER.severe(com.error("No delimiter argument for def-simple\n")); + return FAIL; + } + + String delim = remn; + + try { + BlockReader reader = new SimpleBlockReader(delim, stat.sources.get(sourceName)); + + stat.readers.put(blockName, reader); + } catch (PatternSyntaxException psex) { + LOGGER.severe(com.error("Invalid regular expression '%s' for delimiter. (%s)\n", delim, psex.getMessage())); + return FAIL; + } + + return SUCCESS; + } +} diff --git a/base/src/main/java/bjc/utils/cli/objects/Command.java b/base/src/main/java/bjc/utils/cli/objects/Command.java new file mode 100644 index 0000000..e605a2b --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/objects/Command.java @@ -0,0 +1,87 @@ +package bjc.utils.cli.objects; + +public class Command { + /** + * Command status values. + */ + public static enum CommandStatus { + /** + * The command succeded. + */ + SUCCESS, + /** + * The command failed non-fatally. + */ + FAIL, + /** + * The command failed fatally. + */ + ERROR, + /** + * The command was the last one. + */ + FINISH, + } + + public final int lineNo; + + public final String fullCommand; + public final String remnCommand; + public final String nameCommand; + + public final String ioSource; + + /** + * Create a new command. + * + * @param ln + * The line to get the command from. + * @param lno + * The number of the line the command came from. + * @param ioSrc + * The name of where the I/O came from. + */ + public Command(String ln, int lno, String ioSrc) { + int idx = ln.indexOf(' '); + + if(idx == -1) idx = ln.length(); + + fullCommand = ln; + nameCommand = ln.substring(0, idx).trim(); + remnCommand = ln.substring(idx).trim(); + + lineNo = lno; + + ioSource = ioSrc; + } + + public static Command fromString(String ln, int lno, String ioSource) { + /* + * Ignore blank lines and comments. + */ + if(ln.equals("")) return null; + if(ln.startsWith("#")) return null; + + /* + * Trim off comments part-way through the line. + */ + int idxHash = ln.indexOf('#'); + if(idxHash != -1) { + ln = ln.substring(0, idxHash).trim(); + } + + return new Command(ln, lno, ioSource); + } + + public String warn(String warning, Object... parms) { + String msg = String.format(warning, parms); + + return String.format("WARNING (%s:%d): %s", ioSource, lineNo, msg); + } + + public String error(String err, Object... parms) { + String msg = String.format(err, parms); + + return String.format("ERROR (%s:%d): %s", ioSource, lineNo, msg); + } +} diff --git a/base/src/main/java/bjc/utils/cli/objects/DefineCLI.java b/base/src/main/java/bjc/utils/cli/objects/DefineCLI.java new file mode 100644 index 0000000..bb2733f --- /dev/null +++ b/base/src/main/java/bjc/utils/cli/objects/DefineCLI.java @@ -0,0 +1,133 @@ +package bjc.utils.cli.objects; + +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.function.UnaryOperator; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +import static bjc.utils.cli.objects.Command.CommandStatus; +import static bjc.utils.cli.objects.Command.CommandStatus.*; + +public class DefineCLI { + private final Logger LOGGER = Logger.getLogger(DefineCLI.class.getName()); + + public static class DefineState { + public final Map> defines; + + public final Map strings; + public final Map formats; + + public final Map patterns; + + public DefineState() { + this(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>()); + } + + public DefineState(Map> defines, + Map strings, Map formats, + Map patterns) { + this.defines = defines; + + this.strings = strings; + this.formats = formats; + + this.patterns = patterns; + } + } + + private DefineState stat; + + public DefineCLI() { + stat = new DefineState(); + } + + public static void main(String[] args) { + DefineCLI defin = new DefineCLI(); + } + + /** + * Run the CLI on an input source. + * + * @param input + * The place to read input from. + * @param ioSource + * The name of the place to read input from. + * @param interactive + * Whether or not the source is interactive + */ + public void run(Scanner input, String ioSource, boolean interactive) { + int lno = 0; + while(input.hasNextLine()) { + if(interactive) + System.out.printf("define-conf(%d)>", lno); + + String ln = input.nextLine(); + + lno += 1; + + Command com = Command.fromString(ln, lno, ioSource); + if(com == null) continue; + + handleCommand(com, interactive); + } + + input.close(); + } + + public void handleCommand(Command com, boolean interactive) { + switch(com.nameCommand) { + case "def-string": + default: + LOGGER.severe(com.error("Unknown command %s\n", com.nameCommand)); + break; + } + } + + private CommandStatus defString(Command com) { + String remn = com.remnCommand; + + int idx = remn.indexOf(' '); + if(idx == -1) { + LOGGER.warning(com.warn("Binding empty string to name '%s'\n", remn)); + idx = remn.length(); + } + String name = remn.substring(0, idx); + String strang = remn.substring(idx); + + if(stat.strings.containsKey(name)) { + LOGGER.warning(com.warn("Shadowing string '%s'\n", name)); + } + + stat.strings.put(name, strang); + + return SUCCESS; + } + + private CommandStatus defFormat(Command com) { + String remn = com.remnCommand; + + int idx = remn.indexOf(' '); + if(idx == -1) { + LOGGER.warning(com.warn("Binding empty format to name '%s'\n", remn)); + idx = remn.length(); + } + String name = remn.substring(0, idx); + String fmt = remn.substring(idx); + + if(stat.formats.containsKey(name)) { + LOGGER.warning(com.warn("Shadowing format '%s'\n", name)); + } + + stat.formats.put(name, fmt); + + return SUCCESS; + } + + private CommandStatus bindFormat(Command com) { + String[] parts = com.remnCommand.split(" "); + + return SUCCESS; + } +} diff --git a/base/src/main/java/bjc/utils/components/ComponentDescription.java b/base/src/main/java/bjc/utils/components/ComponentDescription.java new file mode 100644 index 0000000..28f81d1 --- /dev/null +++ b/base/src/main/java/bjc/utils/components/ComponentDescription.java @@ -0,0 +1,135 @@ +package bjc.utils.components; + +/** + * Generic implementation of a description for a component + * + * @author ben + * + */ +public class ComponentDescription implements IDescribedComponent { + private static void sanityCheckArgs(final String name, final String author, final String description, + final int version) { + if (name == null) + throw new NullPointerException("Component name can't be null"); + else if (version <= 0) throw new IllegalArgumentException("Component version must be greater than 0"); + } + + /** + * The author of the component + */ + private final String author; + /** + * The description of the component + */ + private final String description; + /** + * The name of the component + */ + private final String name; + + /** + * The version of the component + */ + private final int version; + + /** + * Create a new component description + * + * @param name + * The name of the component + * @param author + * The author of the component + * @param description + * The description of the component + * @param version + * The version of the component + * @throws IllegalArgumentException + * thrown if version is less than 1 + */ + public ComponentDescription(final String name, final String author, final String description, + final int version) { + sanityCheckArgs(name, author, description, version); + + this.name = name; + this.author = author; + this.description = description; + this.version = version; + } + + @Override + public String getAuthor() { + if (author == null) return IDescribedComponent.super.getAuthor(); + + return author; + } + + @Override + public String getDescription() { + if (description == null) return IDescribedComponent.super.getDescription(); + + return description; + } + + @Override + public String getName() { + return name; + } + + @Override + public int getVersion() { + return version; + } + + @Override + public String toString() { + return name + " component v" + version + ", written by " + author; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + (author == null ? 0 : author.hashCode()); + result = prime * result + (description == null ? 0 : description.hashCode()); + result = prime * result + (name == null ? 0 : name.hashCode()); + result = prime * result + version; + + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + + final ComponentDescription other = (ComponentDescription) obj; + + if (author == null) { + if (other.author != null) return false; + } else if (!author.equals(other.author)) return false; + + if (description == null) { + if (other.description != null) return false; + } else if (!description.equals(other.description)) return false; + + if (name == null) { + if (other.name != null) return false; + } else if (!name.equals(other.name)) return false; + + if (version != other.version) return false; + + return true; + } +} diff --git a/base/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java b/base/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java new file mode 100644 index 0000000..f7ddaff --- /dev/null +++ b/base/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java @@ -0,0 +1,65 @@ +package bjc.utils.components; + +import static bjc.utils.ioutils.RuleBasedReaderPragmas.buildInteger; +import static bjc.utils.ioutils.RuleBasedReaderPragmas.buildStringCollapser; + +import java.io.InputStream; + +import bjc.utils.ioutils.RuleBasedConfigReader; + +/** + * Read a component description from a file + * + * @author ben + * + */ +public class ComponentDescriptionFileParser { + // The reader used to read in component descriptions + private static RuleBasedConfigReader reader; + + // Initialize the reader and its pragmas + static { + // This reader works entirely off of pragmas, so no need to + // handle + // rules + reader = new RuleBasedConfigReader<>((tokenizer, statePair) -> { + // Don't need to do anything on rule start + }, (tokenizer, state) -> { + // Don't need to do anything on rule continuation + }, (state) -> { + // Don't need to do anything on rule end + }); + + setupReaderPragmas(); + } + + /** + * Parse a component description from a stream + * + * @param inputSource + * The stream to parse from + * @return The description parsed from the stream + */ + public static ComponentDescription fromStream(final InputStream inputSource) { + if (inputSource == null) throw new NullPointerException("Input source must not be null"); + + final ComponentDescriptionState readState = reader.fromStream(inputSource, + new ComponentDescriptionState()); + + return readState.toDescription(); + } + + /* + * Create all the pragmas the reader needs to function + */ + private static void setupReaderPragmas() { + reader.addPragma("name", buildStringCollapser("name", (name, state) -> state.setName(name))); + + reader.addPragma("author", buildStringCollapser("author", (author, state) -> state.setAuthor(author))); + + reader.addPragma("description", buildStringCollapser("description", + (description, state) -> state.setDescription(description))); + + reader.addPragma("version", buildInteger("version", (version, state) -> state.setVersion(version))); + } +} diff --git a/base/src/main/java/bjc/utils/components/ComponentDescriptionState.java b/base/src/main/java/bjc/utils/components/ComponentDescriptionState.java new file mode 100644 index 0000000..8d66f85 --- /dev/null +++ b/base/src/main/java/bjc/utils/components/ComponentDescriptionState.java @@ -0,0 +1,144 @@ +package bjc.utils.components; + +/** + * Internal state of component description parser + * + * @author ben + * + */ +public class ComponentDescriptionState { + // Tentative name of this component + private String name; + + // Tentative description of this componet + private String description; + + // Tentative author of this component + private String author; + + // Tentative version of this component + private int version; + + /** + * Set the author of this component + * + * @param author + * The author of this component + */ + public void setAuthor(final String author) { + this.author = author; + } + + /** + * Set the description of this component + * + * @param description + * The description of this component + */ + public void setDescription(final String description) { + this.description = description; + } + + /** + * Set the name of this component + * + * @param name + * The name of this component + */ + public void setName(final String name) { + this.name = name; + } + + /** + * Set the version of this component + * + * @param version + * The version of this component + */ + public void setVersion(final int version) { + this.version = version; + } + + /** + * Convert this state into the description it represents + * + * @return The description represented by this state + */ + public ComponentDescription toDescription() { + return new ComponentDescription(name, author, description, version); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + (author == null ? 0 : author.hashCode()); + result = prime * result + (description == null ? 0 : description.hashCode()); + result = prime * result + (name == null ? 0 : name.hashCode()); + result = prime * result + version; + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + + final ComponentDescriptionState other = (ComponentDescriptionState) obj; + + if (author == null) { + if (other.author != null) return false; + } else if (!author.equals(other.author)) return false; + + if (description == null) { + if (other.description != null) return false; + } else if (!description.equals(other.description)) return false; + + if (name == null) { + if (other.name != null) return false; + } else if (!name.equals(other.name)) return false; + + if (version != other.version) return false; + + return true; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("ComponentDescriptionState ["); + + if (name != null) { + builder.append("name="); + builder.append(name); + builder.append(", "); + } + + if (description != null) { + builder.append("description="); + builder.append(description); + builder.append(", "); + } + + if (author != null) { + builder.append("author="); + builder.append(author); + builder.append(", "); + } + + builder.append("version="); + builder.append(version); + builder.append("]"); + + return builder.toString(); + } + +} diff --git a/base/src/main/java/bjc/utils/components/FileComponentRepository.java b/base/src/main/java/bjc/utils/components/FileComponentRepository.java new file mode 100644 index 0000000..efde5c7 --- /dev/null +++ b/base/src/main/java/bjc/utils/components/FileComponentRepository.java @@ -0,0 +1,181 @@ +package bjc.utils.components; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.function.BiPredicate; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +import bjc.utils.data.IHolder; +import bjc.utils.data.Identity; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; +import bjc.utils.funcutils.FileUtils; + +/** + * A component repository that loads its components from files in a directory + * + * @author ben + * + * @param + * The type of component being read in + */ +public class FileComponentRepository + implements IComponentRepository { + // The logger to use for storing data about this class + private static final Logger CLASS_LOGGER = Logger.getLogger("FileComponentRepository"); + + // The internal storage of components + private IMap components; + + // The path that all the components came from + private Path sourceDirectory; + + /** + * Create a new component repository sourcing components from files in a + * directory + * + * An exception thrown during the loading of a component will only cause + * the loading of that component to fail, but a warning will be logged. + * + * @param directory + * The directory to read component files from + * @param componentReader + * The function to use to convert files to components + */ + public FileComponentRepository(final File directory, + final Function componentReader) { + // Make sure we have valid arguments + if (directory == null) + throw new NullPointerException("Directory must not be null"); + else if (!directory.isDirectory()) + throw new IllegalArgumentException("File " + directory + " is not a directory.\n" + + "Components can only be read from a directory"); + else if (componentReader == null) throw new NullPointerException("Component reader must not be null"); + + // Initialize our fields + components = new FunctionalMap<>(); + sourceDirectory = directory.toPath().toAbsolutePath(); + + // Marker for making sure we don't skip the parent + final IHolder isFirstDir = new Identity<>(true); + + // Predicate to use to traverse all the files in a directory, + // but + // not recurse into sub-directories + final BiPredicate firstLevelTraverser = (pth, attr) -> { + if (attr.isDirectory() && !isFirstDir.getValue()) /* + * Skip + * directories, + * they + * probably + * have + * component + * support + * files. + */ + return false; + + /* + * Don't skip the first directory, that's the parent + * directory + */ + isFirstDir.replace(false); + + return true; + }; + + // Try reading components + try { + FileUtils.traverseDirectory(sourceDirectory, firstLevelTraverser, (pth, attr) -> { + loadComponent(componentReader, pth); + + // Keep loading components, even if this one + // failed + return true; + }); + } catch (final IOException ioex) { + CLASS_LOGGER.log(Level.WARNING, ioex, () -> "Error found reading component from file."); + } + } + + @Override + public IMap getAll() { + return components; + } + + @Override + public ComponentType getByName(final String name) { + return components.get(name); + } + + @Override + public IList getList() { + return components.valueList(); + } + + @Override + public String getSource() { + return "Components read from directory " + sourceDirectory + "."; + } + + /* + * Load a component from a file + */ + private void loadComponent(final Function componentReader, final Path pth) { + try { + // Try to load the component + final ComponentType component = componentReader.apply(pth.toFile()); + + if (component == null) + throw new NullPointerException("Component reader read null component"); + else if (!components.containsKey(component.getName())) { + // We only care about the latest version of a + // component + final ComponentType oldComponent = components.put(component.getName(), component); + + if (oldComponent.getVersion() > component.getVersion()) { + components.put(oldComponent.getName(), oldComponent); + } + } else { + CLASS_LOGGER.warning("Found a duplicate component.\n" + + "Multiple versions of the same component are not currently supported.\n" + + "Only the latest version of the component" + component + + " will be registered ."); + } + } catch (final Exception ex) { + CLASS_LOGGER.log(Level.WARNING, ex, () -> "Error found reading component from file " + + pth.toString() + ". This component will not be loaded"); + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("FileComponentRepository ["); + + if (components != null) { + builder.append("components="); + builder.append(components); + builder.append(", "); + } + + if (sourceDirectory != null) { + builder.append("sourceDirectory="); + builder.append(sourceDirectory); + } + + builder.append("]"); + + return builder.toString(); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/components/IComponentRepository.java b/base/src/main/java/bjc/utils/components/IComponentRepository.java new file mode 100644 index 0000000..6ee51f3 --- /dev/null +++ b/base/src/main/java/bjc/utils/components/IComponentRepository.java @@ -0,0 +1,49 @@ +package bjc.utils.components; + +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; + +/** + * A collection of implementations of a particular type of + * {@link IDescribedComponent} + * + * @author ben + * + * @param + * The type of components contained in this repository + */ +public interface IComponentRepository { + /** + * Get all of the components this repository knows about + * + * @return A map from component name to component, containing all of the + * components in the repositories + */ + public IMap getAll(); + + /** + * Get a component with a specific name + * + * @param name + * The name of the component to retrieve + * @return The named component, or null if no component with that name + * exists + */ + public ComponentType getByName(String name); + + /** + * Get a list of all the registered components + * + * @return A list of all the registered components + */ + public default IList getList() { + return getAll().valueList(); + } + + /** + * Get the source from which these components came + * + * @return The source from which these components came + */ + public String getSource(); +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/components/IDescribedComponent.java b/base/src/main/java/bjc/utils/components/IDescribedComponent.java new file mode 100644 index 0000000..952b375 --- /dev/null +++ b/base/src/main/java/bjc/utils/components/IDescribedComponent.java @@ -0,0 +1,64 @@ +package bjc.utils.components; + +/** + * Represents a optional component that has status information associated with + * it + * + * @author ben + * + */ +public interface IDescribedComponent extends Comparable { + /** + * Get the author of this component + * + * Providing this is optional, with "Anonymous" as the default author + * + * @return The author of the component + */ + default String getAuthor() { + return "Anonymous"; + } + + /** + * Get the description of this component + * + * Providing this is optional, with the default being a note that no + * description was provided + * + * @return The description of the component + */ + default String getDescription() { + return "No description provided."; + } + + /** + * Get the name of this component. + * + * This is the only thing required of all components + * + * @return The name of the component + */ + String getName(); + + /** + * Get the version of this component + * + * Providing this is optional, with "1" as the default version + * + * @return The version of this component + */ + default int getVersion() { + return 1; + } + + @Override + default int compareTo(final IDescribedComponent o) { + int res = getName().compareTo(o.getName()); + + if (res == 0) { + res = getVersion() - o.getVersion(); + } + + return res; + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/data/BooleanToggle.java b/base/src/main/java/bjc/utils/data/BooleanToggle.java new file mode 100644 index 0000000..12e3b2e --- /dev/null +++ b/base/src/main/java/bjc/utils/data/BooleanToggle.java @@ -0,0 +1,76 @@ +package bjc.utils.data; + +/** + * A simple {@link ValueToggle} that swaps between true and false. + * + * @author EVE + * + */ +public class BooleanToggle implements Toggle { + private boolean val; + + /** + * Create a new, initially false, flip-flop. + */ + public BooleanToggle() { + this(false); + } + + /** + * Create a flip-flop with the specified initial value. + * + * @param initial + * The initial value of the flip-flop. + */ + public BooleanToggle(final boolean initial) { + val = initial; + } + + @Override + public Boolean get() { + final boolean res = val; + + val = !res; + + return res; + } + + @Override + public Boolean peek() { + return val; + } + + @Override + public void set(final boolean vl) { + val = vl; + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + + result = prime * result + (val ? 1231 : 1237); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof BooleanToggle)) return false; + + final BooleanToggle other = (BooleanToggle) obj; + + if (val != other.val) return false; + + return true; + } + + @Override + public String toString() { + return String.format("BooleanToggle [val=%s]", val); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/data/CircularIterator.java b/base/src/main/java/bjc/utils/data/CircularIterator.java new file mode 100644 index 0000000..a708eba --- /dev/null +++ b/base/src/main/java/bjc/utils/data/CircularIterator.java @@ -0,0 +1,81 @@ +package bjc.utils.data; + +import java.util.Iterator; + +/** + * An iterator that repeats elements from a provided iterable. + * + * @author EVE + * + * @param + * The type of the iterable. + */ +public class CircularIterator implements Iterator { + /* + * The iterable, and our current iterator into it. + */ + private Iterable source; + private Iterator curr; + + /* + * Our current element. + */ + private E curElm; + + /* + * Should we actually get new iterators, or just repeat the last + * element? + */ + private boolean doCircle; + + /** + * Create a new circular iterator. + * + * @param src + * The iterable to iterate from. + * + * @param circ + * Should we actually do circular iteration, or just + * repeat the terminal element? + */ + public CircularIterator(final Iterable src, final boolean circ) { + source = src; + curr = source.iterator(); + + doCircle = circ; + } + + /** + * Create a new circular iterator that does actual circular iteration. + * + * @param src + * The iterable to iterate from. + */ + public CircularIterator(final Iterable src) { + this(src, true); + } + + @Override + public boolean hasNext() { + // We always have something + return true; + } + + @Override + public E next() { + if (!curr.hasNext()) { + if (doCircle) { + curr = source.iterator(); + } else return curElm; + } + + curElm = curr.next(); + + return curElm; + } + + @Override + public void remove() { + curr.remove(); + } +} diff --git a/base/src/main/java/bjc/utils/data/Either.java b/base/src/main/java/bjc/utils/data/Either.java new file mode 100644 index 0000000..36b3324 --- /dev/null +++ b/base/src/main/java/bjc/utils/data/Either.java @@ -0,0 +1,173 @@ +package bjc.utils.data; + +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * Represents a pair where only one side has a value + * + * @author ben + * @param + * The type that could be on the left + * @param + * The type that could be on the right + * + */ +public class Either implements IPair { + /** + * Create a new either with the left value occupied + * + * @param + * The type of the left value + * @param + * The type of the empty right value + * @param left + * The value to put on the left + * @return An either with the left side occupied + */ + public static Either left(final LeftType left) { + return new Either<>(left, null); + } + + /** + * Create a new either with the right value occupied + * + * @param + * The type of the empty left value + * @param + * The type of the right value + * @param right + * The value to put on the right + * @return An either with the right side occupied + */ + public static Either right(final RightType right) { + return new Either<>(null, right); + } + + private LeftType leftVal; + + private RightType rightVal; + + private boolean isLeft; + + private Either(final LeftType left, final RightType right) { + if (left == null) { + rightVal = right; + } else { + leftVal = left; + + isLeft = true; + } + } + + @Override + public IPair bind( + final BiFunction> binder) { + if (binder == null) throw new NullPointerException("Binder must not be null"); + + return binder.apply(leftVal, rightVal); + } + + @Override + public IPair bindLeft( + final Function> leftBinder) { + if (leftBinder == null) throw new NullPointerException("Left binder must not be null"); + + if (isLeft) return leftBinder.apply(leftVal); + + return new Either<>(null, rightVal); + } + + @Override + public IPair bindRight( + final Function> rightBinder) { + if (rightBinder == null) throw new NullPointerException("Right binder must not be null"); + + if (isLeft) return new Either<>(leftVal, null); + + return rightBinder.apply(rightVal); + } + + @Override + public IPair combine( + final IPair otherPair, + final BiFunction leftCombiner, + final BiFunction rightCombiner) { + if (otherPair == null) + throw new NullPointerException("Other pair must not be null"); + else if (leftCombiner == null) + throw new NullPointerException("Left combiner must not be null"); + else if (rightCombiner == null) throw new NullPointerException("Right combiner must not be null"); + + if (isLeft) return otherPair.bind((otherLeft, otherRight) -> { + return new Either<>(leftCombiner.apply(leftVal, otherLeft), null); + }); + + return otherPair.bind((otherLeft, otherRight) -> { + return new Either<>(null, rightCombiner.apply(rightVal, otherRight)); + }); + } + + @Override + public IPair mapLeft(final Function mapper) { + if (mapper == null) throw new NullPointerException("Mapper must not be null"); + + if (isLeft) return new Either<>(mapper.apply(leftVal), null); + + return new Either<>(null, rightVal); + } + + @Override + public IPair mapRight(final Function mapper) { + if (mapper == null) throw new NullPointerException("Mapper must not be null"); + + if (isLeft) return new Either<>(leftVal, null); + + return new Either<>(null, mapper.apply(rightVal)); + } + + @Override + public MergedType merge(final BiFunction merger) { + if (merger == null) throw new NullPointerException("Merger must not be null"); + + return merger.apply(leftVal, rightVal); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (isLeft ? 1231 : 1237); + result = prime * result + (leftVal == null ? 0 : leftVal.hashCode()); + result = prime * result + (rightVal == null ? 0 : rightVal.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof Either)) return false; + + final Either other = (Either) obj; + + if (isLeft != other.isLeft) return false; + + if (leftVal == null) { + if (other.leftVal != null) return false; + } else if (!leftVal.equals(other.leftVal)) return false; + + if (rightVal == null) { + if (other.rightVal != null) return false; + } else if (!rightVal.equals(other.rightVal)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("Either [leftVal='%s', rightVal='%s', isLeft=%s]", leftVal, rightVal, isLeft); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/data/GeneratingIterator.java b/base/src/main/java/bjc/utils/data/GeneratingIterator.java new file mode 100644 index 0000000..9abca7c --- /dev/null +++ b/base/src/main/java/bjc/utils/data/GeneratingIterator.java @@ -0,0 +1,53 @@ +package bjc.utils.data; + +import java.util.Iterator; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +/** + * An iterator that generates a series of elements from a single element. + * + * @author bjculkin + * + * @param + * The type of element generated. + */ +public class GeneratingIterator implements Iterator { + private E state; + + private UnaryOperator transtion; + + private Predicate stpper; + + /** + * Create a new generative iterator. + * + * @param initial + * The initial state of the generator. + * + * @param transition + * The function to apply to the state. + * + * @param stopper + * The predicate applied to the current state to + * determine when to stop. + */ + public GeneratingIterator(E initial, UnaryOperator transition, Predicate stopper) { + state = initial; + transtion = transition; + stpper = stopper; + } + + @Override + public boolean hasNext() { + return stpper.test(state); + } + + @Override + public E next() { + state = transtion.apply(state); + + return state; + } + +} diff --git a/base/src/main/java/bjc/utils/data/IHolder.java b/base/src/main/java/bjc/utils/data/IHolder.java new file mode 100644 index 0000000..ca0b2ba --- /dev/null +++ b/base/src/main/java/bjc/utils/data/IHolder.java @@ -0,0 +1,153 @@ +package bjc.utils.data; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import bjc.utils.data.internals.BoundListHolder; +import bjc.utils.data.internals.WrappedLazy; +import bjc.utils.data.internals.WrappedOption; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.theory.Functor; + +/** + * A holder of a single value. + * + * @author ben + * + * @param + * The type of value held + */ +public interface IHolder extends Functor { + /** + * Bind a function across the value in this container + * + * @param + * The type of value in this container + * @param binder + * The function to bind to the value + * @return A holder from binding the value + */ + public IHolder bind(Function> binder); + + /** + * Apply an action to the value + * + * @param action + * The action to apply to the value + */ + public default void doWith(final Consumer action) { + transform(value -> { + action.accept(value); + + return value; + }); + } + + @Override + default Function, Functor> fmap( + final Function func) { + return argumentFunctor -> { + if (!(argumentFunctor instanceof IHolder)) { + final String msg = "This functor only supports mapping over instances of IHolder"; + + throw new IllegalArgumentException(msg); + } + + final IHolder holder = (IHolder) argumentFunctor; + + return holder.map(func); + }; + } + + @Override + public default ContainedType getValue() { + return unwrap(value -> value); + } + + /** + * Lifts a function to bind over this holder + * + * @param + * The type of the functions return + * @param func + * The function to lift over the holder + * @return The function lifted over the holder + */ + public Function> lift(Function func); + + /** + * Make this holder lazy + * + * @return A lazy version of this holder + */ + public default IHolder makeLazy() { + return new WrappedLazy<>(this); + } + + /** + * Make this holder a list + * + * @return A list version of this holder + */ + public default IHolder makeList() { + return new BoundListHolder<>(new FunctionalList<>(this)); + } + + /** + * Make this holder optional + * + * @return An optional version of this holder + */ + public default IHolder makeOptional() { + return new WrappedOption<>(this); + } + + /** + * Create a new holder with a mapped version of the value in this + * holder. + * + * Does not change the internal state of this holder + * + * @param + * The type of the mapped value + * @param mapper + * The function to do mapping with + * @return A holder with the mapped value + */ + public IHolder map(Function mapper); + + /** + * Replace the held value with a new one + * + * @param newValue + * The value to hold instead + * @return The holder itself + */ + public default IHolder replace(final ContainedType newValue) { + return transform(oldValue -> { + return newValue; + }); + } + + /** + * Transform the value held in this holder + * + * @param transformer + * The function to transform the value with + * @return The holder itself, for easy chaining + */ + public IHolder transform(UnaryOperator transformer); + + /** + * Unwrap the value contained in this holder so that it is no longer + * held + * + * @param + * The type of the unwrapped value + * @param unwrapper + * The function to use to unwrap the value + * @return The unwrapped held value + */ + public UnwrappedType unwrap(Function unwrapper); +} diff --git a/base/src/main/java/bjc/utils/data/IPair.java b/base/src/main/java/bjc/utils/data/IPair.java new file mode 100644 index 0000000..db8a1cb --- /dev/null +++ b/base/src/main/java/bjc/utils/data/IPair.java @@ -0,0 +1,200 @@ +package bjc.utils.data; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; + +import bjc.utils.funcdata.theory.Bifunctor; + +/** + * Represents a pair of values + * + * @author ben + * @param + * The type of the left side of the pair + * @param + * The type of the right side of the pair + * + */ +public interface IPair extends Bifunctor { + /** + * Bind a function across the values in this pair + * + * @param + * The type of the bound left + * @param + * The type of the bound right + * @param binder + * The function to bind with + * @return The bound pair + */ + public IPair bind( + BiFunction> binder); + + /** + * Bind a function to the left value in this pair + * + * @param + * The type of the bound value + * @param leftBinder + * The function to use to bind + * @return A pair with the left type bound + */ + public IPair bindLeft( + Function> leftBinder); + + /** + * Bind a function to the right value in this pair + * + * @param + * The type of the bound value + * @param rightBinder + * The function to use to bind + * @return A pair with the right type bound + */ + public IPair bindRight( + Function> rightBinder); + + /** + * Pairwise combine two pairs together + * + * @param + * The left type of the other pair + * @param + * The right type of the other pair + * @param otherPair + * The pair to combine with + * @return The pairs, pairwise combined together + */ + public default IPair, IPair> combine( + final IPair otherPair) { + return combine(otherPair, Pair::new, Pair::new); + } + + /** + * Combine the contents of two pairs together + * + * @param + * The type of the left value of the other pair + * @param + * The type of the right value of the other pair + * @param + * The type of the left value of the combined pair + * @param + * The type of the right value of the combined pair + * @param otherPair + * The other pair to combine with + * @param leftCombiner + * @param rightCombiner + * @return A pair with its values combined + */ + public IPair combine( + IPair otherPair, + BiFunction leftCombiner, + BiFunction rightCombiner); + + /** + * Immediately perfom the specified action with the contents of this + * pair + * + * @param consumer + * The action to perform on the pair + */ + public default void doWith(final BiConsumer consumer) { + merge((leftValue, rightValue) -> { + consumer.accept(leftValue, rightValue); + + return null; + }); + } + + @Override + default LeftBifunctorMap fmapLeft( + final Function func) { + return argumentPair -> { + if (!(argumentPair instanceof IPair)) { + final String msg = "This function can only be applied to instances of IPair"; + + throw new IllegalArgumentException(msg); + } + + final IPair argPair = (IPair) argumentPair; + + return argPair.mapLeft(func); + }; + } + + @Override + default RightBifunctorMap + + fmapRight(final Function func) { + return argumentPair -> { + if (!(argumentPair instanceof IPair)) { + final String msg = "This function can only be applied to instances of IPair"; + + throw new IllegalArgumentException(msg); + } + + final IPair argPair = (IPair) argumentPair; + + return argPair.mapRight(func); + }; + } + + /** + * Get the value on the left side of the pair + * + * @return The value on the left side of the pair + */ + @Override + public default LeftType getLeft() { + return merge((leftValue, rightValue) -> leftValue); + } + + /** + * Get the value on the right side of the pair + * + * @return The value on the right side of the pair + */ + @Override + public default RightType getRight() { + return merge((leftValue, rightValue) -> rightValue); + } + + /** + * Transform the value on the left side of the pair. Doesn't modify the + * pair + * + * @param + * The new type of the left part of the pair + * @param mapper + * The function to use to transform the left part of the + * pair + * @return The pair, with its left part transformed + */ + public IPair mapLeft(Function mapper); + + /** + * Transform the value on the right side of the pair. Doesn't modify the + * pair + * + * @param + * The new type of the right part of the pair + * @param mapper + * The function to use to transform the right part of the + * pair + * @return The pair, with its right part transformed + */ + public IPair mapRight(Function mapper); + + /** + * Merge the two values in this pair into a single value + * + * @param + * The type of the single value + * @param merger + * The function to use for merging + * @return The pair, merged into a single value + */ + public MergedType merge(BiFunction merger); +} diff --git a/base/src/main/java/bjc/utils/data/ITree.java b/base/src/main/java/bjc/utils/data/ITree.java new file mode 100644 index 0000000..ff374e8 --- /dev/null +++ b/base/src/main/java/bjc/utils/data/ITree.java @@ -0,0 +1,234 @@ +package bjc.utils.data; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +import bjc.utils.funcdata.bst.TreeLinearizationMethod; +import bjc.utils.functypes.ListFlattener; + +/** + * A node in a homogeneous tree with a unlimited amount of children. + * + * @author ben + * + * @param + * The type of data contained in the tree nodes. + * + */ +public interface ITree { + /** + * Append a child to this node. + * + * @param child + * The child to append to this node. + */ + void addChild(ITree child); + + /** + * Prepend a child to this node. + * + * @param child + * The child to prepend to this node. + */ + void prependChild(ITree child); + + /** + * Collapse a tree into a single version. + * + * @param + * The intermediate type being folded. + * + * @param + * The type that is the end result. + * + * @param leafTransform + * The function to use to convert leaf values. + * + * @param nodeCollapser + * The function to use to convert internal nodes and + * their children. + * + * @param resultTransformer + * The function to use to convert a state to the returned + * version. + * + * @return The final transformed state. + */ + ReturnedType collapse(Function leafTransform, + Function> nodeCollapser, + Function resultTransformer); + + /** + * Execute a given action for each of this tree's children. + * + * @param action + * The action to execute for each child. + */ + void doForChildren(Consumer> action); + + /** + * Expand the nodes of a tree into trees, and then merge the contents of + * those trees into a single tree. + * + * @param mapper + * The function to use to map values into trees. + * + * @return A tree, with some nodes expanded into trees. + */ + default ITree flatMapTree(final Function> mapper) { + return topDownTransform(dat -> TopDownTransformResult.PUSHDOWN, node -> { + if (node.getChildrenCount() > 0) { + final ITree parent = node.transformHead(mapper); + + node.doForChildren(parent::addChild); + + return parent; + } + + return node.transformHead(mapper); + }); + } + + /** + * Get the specified child of this tree. + * + * @param childNo + * The number of the child to get. + * + * @return The specified child of this tree. + */ + default ITree getChild(final int childNo) { + return transformChild(childNo, child -> child); + } + + /** + * Get a count of the number of direct children this node has. + * + * @return The number of direct children this node has. + */ + int getChildrenCount(); + + /** + * Get the data stored in this node. + * + * @return The data stored in this node. + */ + default ContainedType getHead() { + return transformHead(head -> head); + } + + /** + * Rebuild the tree with the same structure, but different nodes. + * + * @param + * The type of the new tree. + * + * @param leafTransformer + * The function to use to transform leaf tokens. + * + * @param operatorTransformer + * The function to use to transform internal tokens. + * + * @return The tree, with the nodes changed. + */ + ITree rebuildTree(Function leafTransformer, + Function operatorTransformer); + + /** + * Transform some of the nodes in this tree. + * + * @param nodePicker + * The predicate to use to pick nodes to transform. + * + * @param transformer + * The function to use to transform picked nodes. + */ + void selectiveTransform(Predicate nodePicker, UnaryOperator transformer); + + /** + * Do a top-down transform of the tree. + * + * @param transformPicker + * The function to use to pick how to progress. + * + * @param transformer + * The function used to transform picked subtrees. + * + * @return The tree with the transform applied to picked subtrees. + */ + ITree topDownTransform(Function transformPicker, + UnaryOperator> transformer); + + /** + * Transform one of this nodes children. + * + * @param + * The type of the transformed value. + * + * @param childNo + * The number of the child to transform. + * + * @param transformer + * The function to use to transform the value. + * + * @return The transformed value. + * + * @throws IllegalArgumentException + * if the childNo is out of bounds (0 <= childNo <= + * childCount()). + */ + TransformedType transformChild(int childNo, + Function, TransformedType> transformer); + + /** + * Transform the value that is the head of this node. + * + * @param + * The type of the transformed value. + * + * @param transformer + * The function to use to transform the value. + * + * @return The transformed value. + */ + TransformedType transformHead(Function transformer); + + /** + * Transform the tree into a tree with a different type of token. + * + * @param + * The type of the new tree. + * + * @param transformer + * The function to use to transform tokens. + * + * @return A tree with the token types transformed. + */ + default ITree transformTree(final Function transformer) { + return rebuildTree(transformer, transformer); + } + + /** + * Perform an action on each part of the tree. + * + * @param linearizationMethod + * The way to traverse the tree. + * + * @param action + * The action to perform on each tree node. + */ + void traverse(TreeLinearizationMethod linearizationMethod, Consumer action); + + /** + * Find the farthest to right child that satisfies the given predicate. + * + * @param childPred + * The predicate to satisfy. + * + * @return The index of the right-most child that satisfies the + * predicate, or -1 if one doesn't exist. + */ + int revFind(Predicate> childPred); +} diff --git a/base/src/main/java/bjc/utils/data/Identity.java b/base/src/main/java/bjc/utils/data/Identity.java new file mode 100644 index 0000000..a8c8d70 --- /dev/null +++ b/base/src/main/java/bjc/utils/data/Identity.java @@ -0,0 +1,118 @@ +package bjc.utils.data; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + +/** + * @author ben + * + * @param + */ +/** + * Simple implementation of IHolder that has no hidden behavior + * + * @author ben + * + * @param + * The type contained in the holder + */ +public class Identity implements IHolder { + private ContainedType heldValue; + + /** + * Create a holder holding null + */ + public Identity() { + heldValue = null; + } + + /** + * Create a holder holding the specified value + * + * @param value + * The value to hold + */ + public Identity(final ContainedType value) { + heldValue = value; + } + + @Override + public IHolder bind(final Function> binder) { + return binder.apply(heldValue); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + (heldValue == null ? 0 : heldValue.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof Identity)) return false; + + final Identity other = (Identity) obj; + + if (heldValue == null) { + if (other.heldValue != null) return false; + } else if (!heldValue.equals(other.heldValue)) return false; + + return true; + } + + @Override + public Function> lift(final Function func) { + return (val) -> { + return new Identity<>(func.apply(val)); + }; + } + + @Override + public IHolder map(final Function mapper) { + return new Identity<>(mapper.apply(heldValue)); + } + + @Override + public String toString() { + return String.format("Identity [heldValue=%s]", heldValue); + } + + @Override + public IHolder transform(final UnaryOperator transformer) { + heldValue = transformer.apply(heldValue); + + return this; + } + + @Override + public UnwrappedType unwrap(final Function unwrapper) { + return unwrapper.apply(heldValue); + } + + /** + * Create a new identity container. + * + * @param val + * The contained value. + * + * @return A new identity container. + */ + public static Identity id(final ContainedType val) { + return new Identity<>(val); + } + + /** + * Create a new empty identity container. + * + * @return A new empty identity container. + */ + public static Identity id() { + return new Identity<>(); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/data/Lazy.java b/base/src/main/java/bjc/utils/data/Lazy.java new file mode 100644 index 0000000..ca41b62 --- /dev/null +++ b/base/src/main/java/bjc/utils/data/Lazy.java @@ -0,0 +1,194 @@ +package bjc.utils.data; + +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + +import bjc.utils.data.internals.BoundLazy; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * A holder that holds a means to create a value, but doesn't actually compute + * the value until it's needed + * + * @author ben + * + * @param + */ +public class Lazy implements IHolder { + private Supplier valueSupplier; + + private IList> actions = new FunctionalList<>(); + + private boolean valueMaterialized; + + private ContainedType heldValue; + + /** + * Create a new lazy value from the specified seed value + * + * @param value + * The seed value to use + */ + public Lazy(final ContainedType value) { + heldValue = value; + + valueMaterialized = true; + } + + /** + * Create a new lazy value from the specified value source + * + * @param supp + * The source of a value to use + */ + public Lazy(final Supplier supp) { + valueSupplier = new SingleSupplier<>(supp); + + valueMaterialized = false; + } + + private Lazy(final Supplier supp, final IList> pendingActions) { + valueSupplier = supp; + + actions = pendingActions; + } + + @Override + public IHolder bind(final Function> binder) { + final IList> pendingActions = new FunctionalList<>(); + + actions.forEach(pendingActions::add); + + final Supplier supplier = () -> { + if (valueMaterialized) return heldValue; + + return valueSupplier.get(); + }; + + return new BoundLazy<>(() -> { + return new Lazy<>(supplier, pendingActions); + }, binder); + } + + @Override + public Function> lift(final Function func) { + return val -> { + return new Lazy<>(func.apply(val)); + }; + } + + @Override + public IHolder map(final Function mapper) { + final IList> pendingActions = new FunctionalList<>(); + + actions.forEach(pendingActions::add); + + return new Lazy<>(() -> { + ContainedType currVal = heldValue; + + if (!valueMaterialized) { + currVal = valueSupplier.get(); + } + + return pendingActions.reduceAux(currVal, UnaryOperator::apply, + value -> mapper.apply(value)); + }); + } + + @Override + public String toString() { + if (valueMaterialized) { + if (actions.isEmpty()) + return String.format("value[v='%s']", heldValue); + else return String.format("value[v='%s'] (has pending transforms)", heldValue); + } + + return "(unmaterialized)"; + } + + @Override + public IHolder transform(final UnaryOperator transformer) { + actions.add(transformer); + + return this; + } + + @Override + public UnwrappedType unwrap(final Function unwrapper) { + if (!valueMaterialized) { + heldValue = valueSupplier.get(); + + valueMaterialized = true; + } + + actions.forEach(action -> { + heldValue = action.apply(heldValue); + }); + + actions = new FunctionalList<>(); + + return unwrapper.apply(heldValue); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + (actions == null ? 0 : actions.hashCode()); + result = prime * result + (heldValue == null ? 0 : heldValue.hashCode()); + result = prime * result + (valueMaterialized ? 1231 : 1237); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof Lazy)) return false; + + final Lazy other = (Lazy) obj; + + if (valueMaterialized != other.valueMaterialized) return false; + + if (valueMaterialized) { + if (heldValue == null) { + if (other.heldValue != null) return false; + } else if (!heldValue.equals(other.heldValue)) return false; + } else return false; + + if (actions == null) { + if (other.actions != null) return false; + } else if (actions.getSize() > 0 || other.actions.getSize() > 0) return false; + + return true; + } + + /** + * Create a new lazy container with an already present value. + * + * @param val + * The value for the lazy container. + * + * @return A new lazy container holding that value. + */ + public static Lazy lazy(final ContainedType val) { + return new Lazy<>(val); + } + + /** + * Create a new lazy container with a suspended value. + * + * @param supp + * The suspended value for the lazy container. + * + * @return A new lazy container that will un-suspend the value when + * necessary. + */ + public static Lazy lazy(final Supplier supp) { + return new Lazy<>(supp); + } +} diff --git a/base/src/main/java/bjc/utils/data/LazyPair.java b/base/src/main/java/bjc/utils/data/LazyPair.java new file mode 100644 index 0000000..5cb85f3 --- /dev/null +++ b/base/src/main/java/bjc/utils/data/LazyPair.java @@ -0,0 +1,240 @@ +package bjc.utils.data; + +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +import bjc.utils.data.internals.BoundLazyPair; +import bjc.utils.data.internals.HalfBoundLazyPair; + +/** + * A lazy implementation of a pair + * + * @author ben + * + * @param + * The type on the left side of the pair + * @param + * The type on the right side of the pair + * + */ +public class LazyPair implements IPair { + private LeftType leftValue; + private RightType rightValue; + + private Supplier leftSupplier; + private Supplier rightSupplier; + + private boolean leftMaterialized; + private boolean rightMaterialized; + + /** + * Create a new lazy pair, using the set values + * + * @param leftVal + * The value for the left side of the pair + * @param rightVal + * The value for the right side of the pair + */ + public LazyPair(final LeftType leftVal, final RightType rightVal) { + leftValue = leftVal; + rightValue = rightVal; + + leftMaterialized = true; + rightMaterialized = true; + } + + /** + * Create a new lazy pair from the given value sources + * + * @param leftSupp + * The source for a value on the left side of the pair + * @param rightSupp + * The source for a value on the right side of the pair + */ + public LazyPair(final Supplier leftSupp, final Supplier rightSupp) { + // Use single suppliers to catch double-instantiation bugs + leftSupplier = new SingleSupplier<>(leftSupp); + rightSupplier = new SingleSupplier<>(rightSupp); + + leftMaterialized = false; + rightMaterialized = false; + } + + @Override + public IPair bind( + final BiFunction> binder) { + return new BoundLazyPair<>(leftSupplier, rightSupplier, binder); + } + + @Override + public IPair bindLeft( + final Function> leftBinder) { + final Supplier leftSupp = () -> { + if (leftMaterialized) return leftValue; + + return leftSupplier.get(); + }; + + return new HalfBoundLazyPair<>(leftSupp, leftBinder); + } + + @Override + public IPair bindRight( + final Function> rightBinder) { + final Supplier rightSupp = () -> { + if (rightMaterialized) return rightValue; + + return rightSupplier.get(); + }; + + return new HalfBoundLazyPair<>(rightSupp, rightBinder); + } + + @Override + public IPair combine( + final IPair otherPair, + final BiFunction leftCombiner, + final BiFunction rightCombiner) { + return otherPair.bind((otherLeft, otherRight) -> { + return bind((leftVal, rightVal) -> { + final CombinedLeft left = leftCombiner.apply(leftVal, otherLeft); + final CombinedRight right = rightCombiner.apply(rightVal, otherRight); + + return new LazyPair<>(left, right); + }); + }); + } + + @Override + public LeftType getLeft() { + if (!leftMaterialized) { + leftValue = leftSupplier.get(); + + leftMaterialized = true; + } + + return leftValue; + } + + @Override + public RightType getRight() { + if (!rightMaterialized) { + rightValue = rightSupplier.get(); + + rightMaterialized = true; + } + + return rightValue; + } + + @Override + public IPair mapLeft(final Function mapper) { + final Supplier leftSupp = () -> { + if (leftMaterialized) return mapper.apply(leftValue); + + return mapper.apply(leftSupplier.get()); + }; + + final Supplier rightSupp = () -> { + if (rightMaterialized) return rightValue; + + return rightSupplier.get(); + }; + + return new LazyPair<>(leftSupp, rightSupp); + } + + @Override + public IPair mapRight(final Function mapper) { + final Supplier leftSupp = () -> { + if (leftMaterialized) return leftValue; + + return leftSupplier.get(); + }; + + final Supplier rightSupp = () -> { + if (rightMaterialized) return mapper.apply(rightValue); + + return mapper.apply(rightSupplier.get()); + }; + + return new LazyPair<>(leftSupp, rightSupp); + } + + @Override + public MergedType merge(final BiFunction merger) { + if (!leftMaterialized) { + leftValue = leftSupplier.get(); + + leftMaterialized = true; + } + + if (!rightMaterialized) { + rightValue = rightSupplier.get(); + + rightMaterialized = true; + } + + return merger.apply(leftValue, rightValue); + } + + @Override + public String toString() { + String leftVal; + String rightVal; + + if (leftMaterialized) { + leftVal = leftValue.toString(); + } else { + leftVal = "(un-materialized)"; + } + + if (rightMaterialized) { + rightVal = rightValue.toString(); + } else { + rightVal = "(un-materialized)"; + } + + return String.format("pair[l=%s,r=%s]", leftVal, rightVal); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + (leftMaterialized ? 1231 : 1237); + result = prime * result + (leftValue == null ? 0 : leftValue.hashCode()); + result = prime * result + (rightMaterialized ? 1231 : 1237); + result = prime * result + (rightValue == null ? 0 : rightValue.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof LazyPair)) return false; + + final LazyPair other = (LazyPair) obj; + + if (leftMaterialized != other.leftMaterialized) return false; + + if (leftMaterialized) { + if (leftValue == null) { + if (other.leftValue != null) return false; + } else if (!leftValue.equals(other.leftValue)) return false; + } else return false; + + if (rightMaterialized != other.rightMaterialized) return false; + if (rightMaterialized) { + if (rightValue == null) { + if (other.rightValue != null) return false; + } else if (!rightValue.equals(other.rightValue)) return false; + } else return false; + + return true; + } +} diff --git a/base/src/main/java/bjc/utils/data/ListHolder.java b/base/src/main/java/bjc/utils/data/ListHolder.java new file mode 100644 index 0000000..142057c --- /dev/null +++ b/base/src/main/java/bjc/utils/data/ListHolder.java @@ -0,0 +1,104 @@ +package bjc.utils.data; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import bjc.utils.data.internals.BoundListHolder; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * A holder that represents a set of non-deterministic computations + * + * @author ben + * + * @param + * The type of contained value + */ +public class ListHolder implements IHolder { + private IList heldValues; + + /** + * Create a new list holder + * + * @param values + * The possible values for the computation + */ + @SafeVarargs + public ListHolder(final ContainedType... values) { + heldValues = new FunctionalList<>(); + + if (values != null) { + for (final ContainedType containedValue : values) { + heldValues.add(containedValue); + } + } + } + + private ListHolder(final IList toHold) { + heldValues = toHold; + } + + @Override + public IHolder bind(final Function> binder) { + final IList> boundValues = heldValues.map(binder); + + return new BoundListHolder<>(boundValues); + } + + @Override + public Function> lift(final Function func) { + return val -> { + return new ListHolder<>(new FunctionalList<>(func.apply(val))); + }; + } + + @Override + public IHolder map(final Function mapper) { + final IList mappedValues = heldValues.map(mapper); + + return new ListHolder<>(mappedValues); + } + + @Override + public IHolder transform(final UnaryOperator transformer) { + heldValues = heldValues.map(transformer); + + return this; + } + + @Override + public UnwrappedType unwrap(final Function unwrapper) { + return unwrapper.apply(heldValues.randItem()); + } + + @Override + public String toString() { + return String.format("ListHolder [heldValues=%s]", heldValues); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + (heldValues == null ? 0 : heldValues.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof ListHolder)) return false; + + final ListHolder other = (ListHolder) obj; + + if (heldValues == null) { + if (other.heldValues != null) return false; + } else if (!heldValues.equals(other.heldValues)) return false; + + return true; + } +} diff --git a/base/src/main/java/bjc/utils/data/Option.java b/base/src/main/java/bjc/utils/data/Option.java new file mode 100644 index 0000000..37e0cde --- /dev/null +++ b/base/src/main/java/bjc/utils/data/Option.java @@ -0,0 +1,93 @@ +package bjc.utils.data; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + +/** + * A holder that may or may not contain a value + * + * @author ben + * + * @param + * The type of the value that may or may not be held + */ +public class Option implements IHolder { + private ContainedType held; + + /** + * Create a new optional, using the given initial value + * + * @param seed + * The initial value for the optional + */ + public Option(final ContainedType seed) { + held = seed; + } + + @Override + public IHolder bind(final Function> binder) { + if (held == null) return new Option<>(null); + + return binder.apply(held); + } + + @Override + public Function> lift(final Function func) { + return val -> { + return new Option<>(func.apply(val)); + }; + } + + @Override + public IHolder map(final Function mapper) { + if (held == null) return new Option<>(null); + + return new Option<>(mapper.apply(held)); + } + + @Override + public IHolder transform(final UnaryOperator transformer) { + if (held != null) { + held = transformer.apply(held); + } + + return this; + } + + @Override + public UnwrappedType unwrap(final Function unwrapper) { + if (held == null) return null; + + return unwrapper.apply(held); + } + + @Override + public String toString() { + return String.format("Option [held='%s']", held); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (held == null ? 0 : held.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof Option)) return false; + + final Option other = (Option) obj; + + if (held == null) { + if (other.held != null) return false; + } else if (!held.equals(other.held)) return false; + + return true; + } +} diff --git a/base/src/main/java/bjc/utils/data/Pair.java b/base/src/main/java/bjc/utils/data/Pair.java new file mode 100644 index 0000000..e6796ba --- /dev/null +++ b/base/src/main/java/bjc/utils/data/Pair.java @@ -0,0 +1,135 @@ +package bjc.utils.data; + +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * A pair of values, with nothing special about them. + * + * @author ben + * + * @param + * The type of the left value + * @param + * The type of the right value + */ +public class Pair implements IPair { + // The left value + private LeftType leftValue; + + // The right value + private RightType rightValue; + + /** + * Create a new pair with both sides set to null + */ + public Pair() { + + } + + /** + * Create a new pair with both sides set to the specified values + * + * @param left + * The value of the left side + * @param right + * The value of the right side + */ + public Pair(final LeftType left, final RightType right) { + leftValue = left; + rightValue = right; + } + + @Override + public IPair bind( + final BiFunction> binder) { + if (binder == null) throw new NullPointerException("Binder must not be null."); + + return binder.apply(leftValue, rightValue); + } + + @Override + public IPair bindLeft( + final Function> leftBinder) { + if (leftBinder == null) throw new NullPointerException("Binder must not be null"); + + return leftBinder.apply(leftValue); + } + + @Override + public IPair bindRight( + final Function> rightBinder) { + if (rightBinder == null) throw new NullPointerException("Binder must not be null"); + + return rightBinder.apply(rightValue); + } + + @Override + public IPair combine( + final IPair otherPair, + final BiFunction leftCombiner, + final BiFunction rightCombiner) { + return otherPair.bind((otherLeft, otherRight) -> { + final CombinedLeft left = leftCombiner.apply(leftValue, otherLeft); + final CombinedRight right = rightCombiner.apply(rightValue, otherRight); + + return new Pair<>(left, right); + }); + } + + @Override + public IPair mapLeft(final Function mapper) { + if (mapper == null) throw new NullPointerException("Mapper must not be null"); + + return new Pair<>(mapper.apply(leftValue), rightValue); + } + + @Override + public IPair mapRight(final Function mapper) { + if (mapper == null) throw new NullPointerException("Mapper must not be null"); + + return new Pair<>(leftValue, mapper.apply(rightValue)); + } + + @Override + public MergedType merge(final BiFunction merger) { + if (merger == null) throw new NullPointerException("Merger must not be null"); + + return merger.apply(leftValue, rightValue); + } + + @Override + public String toString() { + return String.format("Pair [leftValue='%s', rightValue='%s']", leftValue, rightValue); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + (leftValue == null ? 0 : leftValue.hashCode()); + result = prime * result + (rightValue == null ? 0 : rightValue.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof Pair)) return false; + + final Pair other = (Pair) obj; + + if (leftValue == null) { + if (other.leftValue != null) return false; + } else if (!leftValue.equals(other.leftValue)) return false; + + if (rightValue == null) { + if (other.rightValue != null) return false; + } else if (!rightValue.equals(other.rightValue)) return false; + + return true; + } +} diff --git a/base/src/main/java/bjc/utils/data/SingleIterator.java b/base/src/main/java/bjc/utils/data/SingleIterator.java new file mode 100644 index 0000000..4069c3f --- /dev/null +++ b/base/src/main/java/bjc/utils/data/SingleIterator.java @@ -0,0 +1,41 @@ +package bjc.utils.data; + +import java.util.Iterator; + +/** + * An iterator that will only ever yield one item. + * + * @author EVE + * + * @param + * The type of the item. + */ +public class SingleIterator implements Iterator { + private final T itm; + + private boolean yielded; + + /** + * Create a iterator that yields a single item. + * + * @param item + * The item to yield. + */ + public SingleIterator(final T item) { + itm = item; + + yielded = false; + } + + @Override + public boolean hasNext() { + return !yielded; + } + + @Override + public T next() { + yielded = true; + + return itm; + } +} diff --git a/base/src/main/java/bjc/utils/data/SingleSupplier.java b/base/src/main/java/bjc/utils/data/SingleSupplier.java new file mode 100644 index 0000000..c675ebf --- /dev/null +++ b/base/src/main/java/bjc/utils/data/SingleSupplier.java @@ -0,0 +1,72 @@ +package bjc.utils.data; + +import java.util.function.Supplier; + +/** + * A supplier that can only supply one value. + * + * Attempting to retrieve another value will cause an exception to be thrown. + * + * @author ben + * + * @param + * The supplied type + */ +public class SingleSupplier implements Supplier { + private static long nextID = 0; + + private final Supplier source; + + private boolean gotten; + + private final long id; + + /* + * This is bad practice, but I want to know where the single + * instantiation was, in case of duplicate initiations. + */ + private Exception instSite; + + /** + * Create a new single supplier from an existing value + * + * @param supp + * The supplier to give a single value from + */ + public SingleSupplier(final Supplier supp) { + source = supp; + + gotten = false; + + id = nextID++; + } + + @Override + public T get() { + if (gotten == true) { + final String msg = String.format( + "Attempted to retrieve value more than once from single supplier #%d", id); + + final IllegalStateException isex = new IllegalStateException(msg); + + isex.initCause(instSite); + + throw isex; + } + + gotten = true; + + try { + throw new IllegalStateException("Previous instantiation here."); + } catch (final IllegalStateException isex) { + instSite = isex; + } + + return source.get(); + } + + @Override + public String toString() { + return String.format("SingleSupplier [source='%s', gotten=%s, id=%s]", source, gotten, id); + } +} diff --git a/base/src/main/java/bjc/utils/data/Toggle.java b/base/src/main/java/bjc/utils/data/Toggle.java new file mode 100644 index 0000000..1e10dae --- /dev/null +++ b/base/src/main/java/bjc/utils/data/Toggle.java @@ -0,0 +1,35 @@ +package bjc.utils.data; + +/** + * A stateful holder that swaps between two values of the same type. + * + * @author EVE + * + * @param + * The value stored in the toggle. + */ +public interface Toggle { + /** + * Retrieve the currently-aligned value of this toggle, and swap the + * alignment. + * + * @return The previously-aligned value. + */ + E get(); + + /** + * Retrieve the currently-aligned value without altering the alignment. + * + * @return The currently-aligned value. + */ + E peek(); + + /** + * Change the alignment of the toggle. + * + * @param isLeft + * Whether the toggle should be left-aligned or not. + */ + void set(boolean isLeft); + +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/data/TopDownTransformIterator.java b/base/src/main/java/bjc/utils/data/TopDownTransformIterator.java new file mode 100644 index 0000000..1b87e52 --- /dev/null +++ b/base/src/main/java/bjc/utils/data/TopDownTransformIterator.java @@ -0,0 +1,208 @@ +package bjc.utils.data; + +import static bjc.utils.data.TopDownTransformResult.RTRANSFORM; + +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.NoSuchElementException; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +/* + * FIXME something is broken in here. fix it. + */ +public class TopDownTransformIterator implements Iterator> { + private final Function picker; + private final BiFunction, Consumer>>, ITree> transform; + + private ITree preParent; + private ITree postParent; + + private final Deque> preChildren; + private final Deque> postChildren; + + private TopDownTransformIterator curChild; + + private boolean done; + private boolean initial; + + private final Deque>> toYield; + private Iterator> curYield; + + public TopDownTransformIterator(final Function pickr, + final BiFunction, Consumer>>, ITree> transfrm, + final ITree tree) { + preParent = tree; + + preChildren = new LinkedList<>(); + postChildren = new LinkedList<>(); + toYield = new LinkedList<>(); + + picker = pickr; + transform = transfrm; + + done = false; + initial = true; + } + + public void addYield(final Iterator> src) { + if (curYield != null) { + toYield.push(curYield); + } + + curYield = src; + } + + @Override + public boolean hasNext() { + return !done; + } + + public ITree flushYields(final ITree val) { + if (curYield != null) { + toYield.add(new SingleIterator<>(val)); + + if (curYield.hasNext()) + return curYield.next(); + else { + while (toYield.size() != 0 && !curYield.hasNext()) { + curYield = toYield.pop(); + } + + if (toYield.size() == 0 && !curYield.hasNext()) { + curYield = null; + return val; + } else return curYield.next(); + } + } else return val; + } + + @Override + public ITree next() { + if (done) throw new NoSuchElementException(); + + if (curYield != null) { + if (curYield.hasNext()) + return curYield.next(); + else { + while (toYield.size() != 0 && !curYield.hasNext()) { + curYield = toYield.pop(); + } + + if (toYield.size() == 0 && !curYield.hasNext()) { + curYield = null; + } else return curYield.next(); + } + } + + if (initial) { + final TopDownTransformResult res = picker.apply(preParent.getHead()); + + switch (res) { + case PASSTHROUGH: + postParent = new Tree<>(preParent.getHead()); + + if (preParent.getChildrenCount() != 0) { + for (int i = 0; i < preParent.getChildrenCount(); i++) { + preChildren.add(preParent.getChild(i)); + } + + // Return whatever the first child is + break; + } else { + done = true; + return flushYields(postParent); + } + case SKIP: + done = true; + return flushYields(preParent); + case TRANSFORM: + done = true; + return flushYields(transform.apply(preParent, this::addYield)); + case RTRANSFORM: + preParent = transform.apply(preParent, this::addYield); + return flushYields(preParent); + case PUSHDOWN: + if (preParent.getChildrenCount() != 0) { + for (int i = 0; i < preParent.getChildrenCount(); i++) { + preChildren.add(preParent.getChild(i)); + } + + // Return whatever the first child is + break; + } else { + done = true; + return flushYields(transform.apply(new Tree<>(preParent.getHead()), + this::addYield)); + } + case PULLUP: + final ITree intRes = transform.apply(preParent, this::addYield); + + postParent = new Tree<>(intRes.getHead()); + + if (intRes.getChildrenCount() != 0) { + for (int i = 0; i < intRes.getChildrenCount(); i++) { + preChildren.add(intRes.getChild(i)); + } + + // Return whatever the first child is + break; + } else { + done = true; + return flushYields(postParent); + } + default: + throw new IllegalArgumentException("Unknown result type " + res); + } + + if (res != RTRANSFORM) { + initial = false; + } + } + + if (curChild == null || !curChild.hasNext()) { + if (preChildren.size() != 0) { + curChild = new TopDownTransformIterator<>(picker, transform, preChildren.pop()); + + final ITree res = curChild.next(); + System.out.println("\t\tTRACE: adding node " + res + " to children"); + postChildren.add(res); + + return flushYields(res); + } else { + ITree res = null; + + if (postParent == null) { + res = new Tree<>(preParent.getHead()); + + System.out.println("\t\tTRACE: adding nodes " + postChildren + " to " + res); + + for (final ITree child : postChildren) { + res.addChild(child); + } + + // res = transform.apply(res, + // this::addYield); + } else { + res = postParent; + + System.out.println("\t\tTRACE: adding nodes " + postChildren + " to " + res); + for (final ITree child : postChildren) { + res.addChild(child); + } + } + + done = true; + return flushYields(res); + } + } else { + final ITree res = curChild.next(); + System.out.println("\t\tTRACE: adding node " + res + " to children"); + postChildren.add(res); + + return flushYields(res); + } + } +} diff --git a/base/src/main/java/bjc/utils/data/TopDownTransformResult.java b/base/src/main/java/bjc/utils/data/TopDownTransformResult.java new file mode 100644 index 0000000..ed41eae --- /dev/null +++ b/base/src/main/java/bjc/utils/data/TopDownTransformResult.java @@ -0,0 +1,34 @@ +package bjc.utils.data; + +/** + * Represents the results for doing a top-down transform of a tree + * + * @author ben + * + */ +public enum TopDownTransformResult { + /** + * Do not do anything to this node, and ignore its children + */ + SKIP, + /** + * Transform this node, and don't touch its children + */ + TRANSFORM, + /** + * Transform this node, then do a top-down transform on the result + */ + RTRANSFORM, + /** + * Ignore this node, and traverse its children + */ + PASSTHROUGH, + /** + * Traverse the nodes of this children, then transform it + */ + PUSHDOWN, + /** + * Transform this node, then traverse its children + */ + PULLUP; +} diff --git a/base/src/main/java/bjc/utils/data/TransformIterator.java b/base/src/main/java/bjc/utils/data/TransformIterator.java new file mode 100644 index 0000000..50f28b1 --- /dev/null +++ b/base/src/main/java/bjc/utils/data/TransformIterator.java @@ -0,0 +1,46 @@ +package bjc.utils.data; + +import java.util.Iterator; +import java.util.function.Function; + +/** + * An iterator that transforms values from one type to another. + * + * @author EVE + * + * @param + * The source iterator type. + * + * @param + * The destination iterator type. + */ +public class TransformIterator implements Iterator { + private final Iterator source; + + private final Function transform; + + /** + * Create a new transform iterator. + * + * @param source + * The source iterator to use. + * + * @param transform + * The transform to apply. + */ + public TransformIterator(final Iterator source, final Function transform) { + this.source = source; + this.transform = transform; + } + + @Override + public boolean hasNext() { + return source.hasNext(); + } + + @Override + public D next() { + return transform.apply(source.next()); + } + +} diff --git a/base/src/main/java/bjc/utils/data/Tree.java b/base/src/main/java/bjc/utils/data/Tree.java new file mode 100644 index 0000000..a52f699 --- /dev/null +++ b/base/src/main/java/bjc/utils/data/Tree.java @@ -0,0 +1,390 @@ +package bjc.utils.data; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.bst.TreeLinearizationMethod; +import bjc.utils.functypes.ListFlattener; + +/** + * A node in a homogeneous tree. + * + * @author ben + * + * @param + */ +public class Tree implements ITree { + private ContainedType data; + + private IList> children; + private boolean hasChildren; + private int childCount = 0; + + private int ID; + private static int nextID = 0; + + /** + * Create a new leaf node in a tree. + * + * @param leaf + * The data to store as a leaf node. + */ + public Tree(final ContainedType leaf) { + data = leaf; + + hasChildren = false; + + ID = nextID++; + } + + /** + * Create a new tree node with the specified children. + * + * @param leaf + * The data to hold in this node. + * + * @param childrn + * A list of children for this node. + */ + public Tree(final ContainedType leaf, final IList> childrn) { + this(leaf); + + hasChildren = true; + + childCount = childrn.getSize(); + + children = childrn; + } + + /** + * Create a new tree node with the specified children. + * + * @param leaf + * The data to hold in this node. + * + * @param childrn + * A list of children for this node. + */ + @SafeVarargs + public Tree(final ContainedType leaf, final ITree... childrn) { + this(leaf); + + hasChildren = true; + + childCount = 0; + + children = new FunctionalList<>(); + + for (final ITree child : childrn) { + children.add(child); + + childCount++; + } + } + + @Override + public void addChild(final ITree child) { + if (hasChildren == false) { + hasChildren = true; + + children = new FunctionalList<>(); + } + + childCount++; + + children.add(child); + } + + @Override + public void prependChild(final ITree child) { + if (hasChildren == false) { + hasChildren = true; + + children = new FunctionalList<>(); + } + + childCount++; + + children.prepend(child); + } + + @Override + public void doForChildren(final Consumer> action) { + if (childCount > 0) { + children.forEach(action); + } + } + + @Override + public int getChildrenCount() { + return childCount; + } + + @Override + public int revFind(final Predicate> childPred) { + if (childCount == 0) + return -1; + else { + for (int i = childCount - 1; i >= 0; i--) { + if (childPred.test(getChild(i))) return i; + } + } + + return -1; + } + + @Override + public void traverse(final TreeLinearizationMethod linearizationMethod, final Consumer action) { + if (hasChildren) { + switch (linearizationMethod) { + case INORDER: + if (childCount != 2) { + final String msg = "Can only do in-order traversal for binary trees."; + + throw new IllegalArgumentException(msg); + } + + children.getByIndex(0).traverse(linearizationMethod, action); + + action.accept(data); + + children.getByIndex(1).traverse(linearizationMethod, action); + break; + case POSTORDER: + children.forEach((child) -> child.traverse(linearizationMethod, action)); + + action.accept(data); + break; + case PREORDER: + action.accept(data); + + children.forEach((child) -> child.traverse(linearizationMethod, action)); + break; + default: + break; + + } + } else { + action.accept(data); + } + } + + @Override + public ReturnedType collapse(final Function leafTransform, + final Function> nodeCollapser, + final Function resultTransformer) { + return resultTransformer.apply(internalCollapse(leafTransform, nodeCollapser)); + } + + @Override + public ITree flatMapTree(final Function> mapper) { + if (hasChildren) { + final ITree flatMappedData = mapper.apply(data); + + final IList> mappedChildren = children + .map(child -> child.flatMapTree(mapper)); + + mappedChildren.forEach(child -> flatMappedData.addChild(child)); + + return flatMappedData; + } + + return mapper.apply(data); + } + + protected NewType internalCollapse(final Function leafTransform, + final Function> nodeCollapser) { + if (hasChildren) { + final Function, NewType> nodeTransformer = nodeCollapser.apply(data); + + final IList collapsedChildren = children.map(child -> { + final NewType collapsed = child.collapse(leafTransform, nodeCollapser, + subTreeVal -> subTreeVal); + + return collapsed; + }); + + return nodeTransformer.apply(collapsedChildren); + } + + return leafTransform.apply(data); + } + + protected void internalToString(final StringBuilder builder, final int indentLevel, final boolean initial) { + for (int i = 0; i < indentLevel; i++) { + builder.append(">\t"); + } + + builder.append("Node #"); + builder.append(ID); + builder.append(": "); + builder.append(data == null ? "(null)" : data.toString()); + builder.append("\n"); + + if (hasChildren) { + children.forEach(child -> { + if (child instanceof Tree) { + final Tree kid = (Tree) child; + + kid.internalToString(builder, indentLevel + 1, false); + } else { + for (int i = 0; i < indentLevel + 1; i++) { + builder.append(">\t"); + } + + builder.append("Unknown node\n"); + } + }); + } + } + + @Override + public ITree rebuildTree(final Function leafTransformer, + final Function operatorTransformer) { + if (hasChildren) { + final IList> mappedChildren = children.map(child -> { + return child.rebuildTree(leafTransformer, operatorTransformer); + }); + + return new Tree<>(operatorTransformer.apply(data), mappedChildren); + } + + return new Tree<>(leafTransformer.apply(data)); + } + + @Override + public void selectiveTransform(final Predicate nodePicker, + final UnaryOperator transformer) { + if (hasChildren) { + children.forEach(child -> child.selectiveTransform(nodePicker, transformer)); + } else { + data = transformer.apply(data); + } + } + + @Override + public ITree topDownTransform( + final Function transformPicker, + final UnaryOperator> transformer) { + final TopDownTransformResult transformResult = transformPicker.apply(data); + + switch (transformResult) { + case PASSTHROUGH: + ITree result = new Tree<>(data); + + if (hasChildren) { + children.forEach(child -> { + final ITree kid = child.topDownTransform(transformPicker, + transformer); + + result.addChild(kid); + }); + } + + return result; + case SKIP: + return this; + case TRANSFORM: + return transformer.apply(this); + case RTRANSFORM: + return transformer.apply(this).topDownTransform(transformPicker, transformer); + case PUSHDOWN: + result = new Tree<>(data); + + if (hasChildren) { + children.forEach(child -> { + final ITree kid = child.topDownTransform(transformPicker, + transformer); + + result.addChild(kid); + }); + } + + return transformer.apply(result); + case PULLUP: + final ITree intermediateResult = transformer.apply(this); + + result = new Tree<>(intermediateResult.getHead()); + + intermediateResult.doForChildren(child -> { + final ITree kid = child.topDownTransform(transformPicker, transformer); + + result.addChild(kid); + }); + + return result; + default: + final String msg = String.format("Recieved unknown transform result %s", transformResult); + + throw new IllegalArgumentException(msg); + } + } + + @Override + public TransformedType transformChild(final int childNo, + final Function, TransformedType> transformer) { + if (childNo < 0 || childNo > childCount - 1) { + final String msg = String.format("Child index #%d is invalid", childNo); + + throw new IllegalArgumentException(msg); + } + + final ITree selectedKid = children.getByIndex(childNo); + + return transformer.apply(selectedKid); + } + + @Override + public TransformedType transformHead( + final Function transformer) { + return transformer.apply(data); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + childCount; + result = prime * result + (children == null ? 0 : children.hashCode()); + result = prime * result + (data == null ? 0 : data.hashCode()); + + return result; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + + internalToString(builder, 1, true); + + builder.deleteCharAt(builder.length() - 1); + + return builder.toString(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof Tree)) return false; + + final Tree other = (Tree) obj; + + if (data == null) { + if (other.data != null) return false; + } else if (!data.equals(other.data)) return false; + + if (childCount != other.childCount) return false; + + if (children == null) { + if (other.children != null) return false; + } else if (!children.equals(other.children)) return false; + + return true; + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/data/ValueToggle.java b/base/src/main/java/bjc/utils/data/ValueToggle.java new file mode 100644 index 0000000..9193896 --- /dev/null +++ b/base/src/main/java/bjc/utils/data/ValueToggle.java @@ -0,0 +1,54 @@ +package bjc.utils.data; + +/** + * A simple implementation of {@link Toggle}. + * + * @author EVE + * + * @param + * The type of value to toggle between. + */ +public class ValueToggle implements Toggle { + private final E lft; + private final E rght; + + private final BooleanToggle alignment; + + /** + * Create a new toggle. + * + * All toggles start right-aligned. + * + * @param left + * The value when the toggle is left-aligned. + * + * @param right + * The value when the toggle is right-aligned. + */ + public ValueToggle(final E left, final E right) { + lft = left; + + rght = right; + + alignment = new BooleanToggle(); + } + + @Override + public E get() { + if (alignment.get()) + return lft; + else return rght; + } + + @Override + public E peek() { + if (alignment.peek()) + return lft; + else return rght; + } + + @Override + public void set(final boolean isLeft) { + alignment.set(isLeft); + } +} diff --git a/base/src/main/java/bjc/utils/data/internals/BoundLazy.java b/base/src/main/java/bjc/utils/data/internals/BoundLazy.java new file mode 100644 index 0000000..f71d32b --- /dev/null +++ b/base/src/main/java/bjc/utils/data/internals/BoundLazy.java @@ -0,0 +1,145 @@ +package bjc.utils.data.internals; + +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + +import bjc.utils.data.IHolder; +import bjc.utils.data.Lazy; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/* + * Implements a lazy holder that has been bound + */ +public class BoundLazy implements IHolder { + /* + * The old value + */ + private final Supplier> oldSupplier; + + /* + * The function to use to transform the old value into a new value + */ + private final Function> binder; + + /* + * The bound value being held + */ + private IHolder boundHolder; + + /* + * Whether the bound value has been actualized or not + */ + private boolean holderBound; + + /* + * Transformations currently pending on the bound value + */ + private final IList> actions = new FunctionalList<>(); + + /* + * Create a new bound lazy value + */ + public BoundLazy(final Supplier> supp, + final Function> binder) { + oldSupplier = supp; + this.binder = binder; + } + + @Override + public IHolder bind(final Function> bindr) { + if (bindr == null) throw new NullPointerException("Binder must not be null"); + + /* + * Prepare a list of pending actions + */ + final IList> pendingActions = new FunctionalList<>(); + actions.forEach(pendingActions::add); + + /* + * Create the new supplier of a value + */ + final Supplier> typeSupplier = () -> { + IHolder oldHolder = boundHolder; + + /* + * Bind the value if it hasn't been bound before + */ + if (!holderBound) { + oldHolder = oldSupplier.get().unwrap(binder); + } + + /* + * Apply all the pending actions + */ + return pendingActions.reduceAux(oldHolder, (action, state) -> { + return state.transform(action); + }, (value) -> value); + }; + + return new BoundLazy<>(typeSupplier, bindr); + } + + @Override + public Function> lift( + final Function func) { + if (func == null) throw new NullPointerException("Function to lift must not be null"); + + return (val) -> { + return new Lazy<>(func.apply(val)); + }; + } + + @Override + public IHolder map(final Function mapper) { + if (mapper == null) throw new NullPointerException("Mapper must not be null"); + + // Prepare a list of pending actions + final IList> pendingActions = new FunctionalList<>(); + actions.forEach(pendingActions::add); + + // Prepare the new supplier + final Supplier typeSupplier = () -> { + IHolder oldHolder = boundHolder; + + // Bound the value if it hasn't been bound + if (!holderBound) { + oldHolder = oldSupplier.get().unwrap(binder); + } + + return pendingActions.reduceAux(oldHolder.getValue(), (action, state) -> { + return action.apply(state); + }, (value) -> mapper.apply(value)); + }; + + return new Lazy<>(typeSupplier); + } + + @Override + public String toString() { + if (holderBound) return boundHolder.toString(); + + return "(unmaterialized)"; + } + + @Override + public IHolder transform(final UnaryOperator transformer) { + if (transformer == null) throw new NullPointerException("Transformer must not be null"); + + actions.add(transformer); + + return this; + } + + @Override + public UnwrappedType unwrap(final Function unwrapper) { + if (unwrapper == null) throw new NullPointerException("Unwrapper must not be null"); + + if (!holderBound) { + boundHolder = oldSupplier.get().unwrap(binder::apply); + } + + return boundHolder.unwrap(unwrapper); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/data/internals/BoundLazyPair.java b/base/src/main/java/bjc/utils/data/internals/BoundLazyPair.java new file mode 100644 index 0000000..df6e60b --- /dev/null +++ b/base/src/main/java/bjc/utils/data/internals/BoundLazyPair.java @@ -0,0 +1,199 @@ +package bjc.utils.data.internals; + +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +import bjc.utils.data.IHolder; +import bjc.utils.data.IPair; +import bjc.utils.data.Identity; +import bjc.utils.data.LazyPair; + +/* + * Implements a lazy pair that has been bound + */ +public class BoundLazyPair implements IPair { + /* + * The supplier of the left value + */ + private final Supplier leftSupplier; + /* + * The supplier of the right value + */ + private final Supplier rightSupplier; + + /* + * The binder to transform values + */ + private final BiFunction> binder; + + /* + * The bound pair + */ + private IPair boundPair; + + /* + * Whether the pair has been bound yet + */ + private boolean pairBound; + + public BoundLazyPair(final Supplier leftSupp, final Supplier rightSupp, + final BiFunction> bindr) { + leftSupplier = leftSupp; + rightSupplier = rightSupp; + binder = bindr; + } + + @Override + public IPair bind( + final BiFunction> bindr) { + if (bindr == null) throw new NullPointerException("Binder must not be null"); + + final IHolder> newPair = new Identity<>(boundPair); + final IHolder newPairMade = new Identity<>(pairBound); + + final Supplier leftSupp = () -> { + if (!newPairMade.getValue()) { + newPair.replace(binder.apply(leftSupplier.get(), rightSupplier.get())); + + newPairMade.replace(true); + } + + return newPair.unwrap((pair) -> pair.getLeft()); + }; + + final Supplier rightSupp = () -> { + if (!newPairMade.getValue()) { + newPair.replace(binder.apply(leftSupplier.get(), rightSupplier.get())); + + newPairMade.replace(true); + } + + return newPair.unwrap((pair) -> pair.getRight()); + }; + + return new BoundLazyPair<>(leftSupp, rightSupp, bindr); + } + + @Override + public IPair bindLeft( + final Function> leftBinder) { + if (leftBinder == null) throw new NullPointerException("Left binder must not be null"); + + final Supplier leftSupp = () -> { + IPair newPair = boundPair; + + if (!pairBound) { + newPair = binder.apply(leftSupplier.get(), rightSupplier.get()); + } + + return newPair.getLeft(); + }; + + return new HalfBoundLazyPair<>(leftSupp, leftBinder); + } + + @Override + public IPair bindRight( + final Function> rightBinder) { + if (rightBinder == null) throw new NullPointerException("Right binder must not be null"); + + final Supplier rightSupp = () -> { + IPair newPair = boundPair; + + if (!pairBound) { + newPair = binder.apply(leftSupplier.get(), rightSupplier.get()); + } + + return newPair.getRight(); + }; + + return new HalfBoundLazyPair<>(rightSupp, rightBinder); + } + + @Override + public IPair combine( + final IPair otherPair, + final BiFunction leftCombiner, + final BiFunction rightCombiner) { + if (otherPair == null) + throw new NullPointerException("Other pair must not be null"); + else if (leftCombiner == null) + throw new NullPointerException("Left combiner must not be null"); + else if (rightCombiner == null) throw new NullPointerException("Right combiner must not be null"); + + return otherPair.bind((otherLeft, otherRight) -> { + return bind((leftVal, rightVal) -> { + return new LazyPair<>(leftCombiner.apply(leftVal, otherLeft), + rightCombiner.apply(rightVal, otherRight)); + }); + }); + } + + @Override + public IPair mapLeft(final Function mapper) { + if (mapper == null) throw new NullPointerException("Mapper must not be null"); + + final Supplier leftSupp = () -> { + if (!pairBound) { + final NewLeft leftVal = binder.apply(leftSupplier.get(), rightSupplier.get()).getLeft(); + + return mapper.apply(leftVal); + } + + return mapper.apply(boundPair.getLeft()); + }; + + final Supplier rightSupp = () -> { + if (!pairBound) return binder.apply(leftSupplier.get(), rightSupplier.get()).getRight(); + + return boundPair.getRight(); + }; + + return new LazyPair<>(leftSupp, rightSupp); + } + + @Override + public IPair mapRight(final Function mapper) { + if (mapper == null) throw new NullPointerException("Mapper must not be null"); + + final Supplier leftSupp = () -> { + if (!pairBound) return binder.apply(leftSupplier.get(), rightSupplier.get()).getLeft(); + + return boundPair.getLeft(); + }; + + final Supplier rightSupp = () -> { + if (!pairBound) { + final NewRight rightVal = binder.apply(leftSupplier.get(), rightSupplier.get()) + .getRight(); + + return mapper.apply(rightVal); + } + + return mapper.apply(boundPair.getRight()); + }; + + return new LazyPair<>(leftSupp, rightSupp); + } + + @Override + public MergedType merge(final BiFunction merger) { + if (merger == null) throw new NullPointerException("Merger must not be null"); + + if (!pairBound) { + boundPair = binder.apply(leftSupplier.get(), rightSupplier.get()); + + pairBound = true; + } + + return boundPair.merge(merger); + } + + @Override + public String toString() { + if (pairBound) return boundPair.toString(); + + return "(un-materialized)"; + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/data/internals/BoundListHolder.java b/base/src/main/java/bjc/utils/data/internals/BoundListHolder.java new file mode 100644 index 0000000..f3799fd --- /dev/null +++ b/base/src/main/java/bjc/utils/data/internals/BoundListHolder.java @@ -0,0 +1,68 @@ +package bjc.utils.data.internals; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import bjc.utils.data.IHolder; +import bjc.utils.data.ListHolder; +import bjc.utils.funcdata.IList; + +/* + * Holds a list, converted into a holder + */ +public class BoundListHolder implements IHolder { + private final IList> heldHolders; + + public BoundListHolder(final IList> toHold) { + heldHolders = toHold; + } + + @Override + public IHolder bind(final Function> binder) { + if (binder == null) throw new NullPointerException("Binder must not be null"); + + final IList> boundHolders = heldHolders.map((containedHolder) -> { + return containedHolder.bind(binder); + }); + + return new BoundListHolder<>(boundHolders); + } + + @Override + public Function> lift(final Function func) { + if (func == null) throw new NullPointerException("Function to lift must not be null"); + + return (val) -> { + return new ListHolder<>(func.apply(val)); + }; + } + + @Override + public IHolder map(final Function mapper) { + if (mapper == null) throw new NullPointerException("Mapper must not be null"); + + final IList> mappedHolders = heldHolders.map((containedHolder) -> { + return containedHolder.map(mapper); + }); + + return new BoundListHolder<>(mappedHolders); + } + + @Override + public IHolder transform(final UnaryOperator transformer) { + if (transformer == null) throw new NullPointerException("Transformer must not be null"); + + heldHolders.forEach((containedHolder) -> { + containedHolder.transform(transformer); + }); + + return this; + } + + @Override + public UnwrappedType unwrap(final Function unwrapper) { + if (unwrapper == null) throw new NullPointerException("Unwrapper must not be null"); + + return heldHolders.randItem().unwrap(unwrapper); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/data/internals/HalfBoundLazyPair.java b/base/src/main/java/bjc/utils/data/internals/HalfBoundLazyPair.java new file mode 100644 index 0000000..8cac38b --- /dev/null +++ b/base/src/main/java/bjc/utils/data/internals/HalfBoundLazyPair.java @@ -0,0 +1,149 @@ +package bjc.utils.data.internals; + +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +import bjc.utils.data.IHolder; +import bjc.utils.data.IPair; +import bjc.utils.data.Identity; +import bjc.utils.data.LazyPair; + +/* + * A lazy pair, with only one side bound + */ +public class HalfBoundLazyPair implements IPair { + private final Supplier oldSupplier; + + private final Function> binder; + + private IPair boundPair; + private boolean pairBound; + + public HalfBoundLazyPair(final Supplier oldSupp, + final Function> bindr) { + oldSupplier = oldSupp; + binder = bindr; + } + + @Override + public IPair bind( + final BiFunction> bindr) { + final IHolder> newPair = new Identity<>(boundPair); + final IHolder newPairMade = new Identity<>(pairBound); + + final Supplier leftSupp = () -> { + if (!newPairMade.getValue()) { + newPair.replace(binder.apply(oldSupplier.get())); + newPairMade.replace(true); + } + + return newPair.unwrap((pair) -> pair.getLeft()); + }; + + final Supplier rightSupp = () -> { + if (!newPairMade.getValue()) { + newPair.replace(binder.apply(oldSupplier.get())); + newPairMade.replace(true); + } + + return newPair.unwrap((pair) -> pair.getRight()); + }; + + return new BoundLazyPair<>(leftSupp, rightSupp, bindr); + } + + @Override + public IPair bindLeft( + final Function> leftBinder) { + final Supplier leftSupp = () -> { + IPair newPair = boundPair; + + if (!pairBound) { + newPair = binder.apply(oldSupplier.get()); + } + + return newPair.getLeft(); + }; + + return new HalfBoundLazyPair<>(leftSupp, leftBinder); + } + + @Override + public IPair bindRight( + final Function> rightBinder) { + final Supplier rightSupp = () -> { + IPair newPair = boundPair; + + if (!pairBound) { + newPair = binder.apply(oldSupplier.get()); + } + + return newPair.getRight(); + }; + + return new HalfBoundLazyPair<>(rightSupp, rightBinder); + } + + @Override + public IPair combine( + final IPair otherPair, + final BiFunction leftCombiner, + final BiFunction rightCombiner) { + return otherPair.bind((otherLeft, otherRight) -> { + return bind((leftVal, rightVal) -> { + return new LazyPair<>(leftCombiner.apply(leftVal, otherLeft), + rightCombiner.apply(rightVal, otherRight)); + }); + }); + } + + @Override + public IPair mapLeft(final Function mapper) { + final Supplier leftSupp = () -> { + if (pairBound) return mapper.apply(boundPair.getLeft()); + + final NewLeft leftVal = binder.apply(oldSupplier.get()).getLeft(); + + return mapper.apply(leftVal); + }; + + final Supplier rightSupp = () -> { + if (pairBound) return boundPair.getRight(); + + return binder.apply(oldSupplier.get()).getRight(); + }; + + return new LazyPair<>(leftSupp, rightSupp); + } + + @Override + public IPair mapRight(final Function mapper) { + final Supplier leftSupp = () -> { + if (pairBound) return boundPair.getLeft(); + + return binder.apply(oldSupplier.get()).getLeft(); + }; + + final Supplier rightSupp = () -> { + if (pairBound) return mapper.apply(boundPair.getRight()); + + final NewRight rightVal = binder.apply(oldSupplier.get()).getRight(); + + return mapper.apply(rightVal); + }; + + return new LazyPair<>(leftSupp, rightSupp); + } + + @Override + public MergedType merge(final BiFunction merger) { + if (!pairBound) { + boundPair = binder.apply(oldSupplier.get()); + + pairBound = true; + } + + return boundPair.merge(merger); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/data/internals/WrappedLazy.java b/base/src/main/java/bjc/utils/data/internals/WrappedLazy.java new file mode 100644 index 0000000..4175724 --- /dev/null +++ b/base/src/main/java/bjc/utils/data/internals/WrappedLazy.java @@ -0,0 +1,62 @@ +package bjc.utils.data.internals; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import bjc.utils.data.IHolder; +import bjc.utils.data.Lazy; + +public class WrappedLazy implements IHolder { + private final IHolder> held; + + public WrappedLazy(final IHolder wrappedHolder) { + held = new Lazy<>(wrappedHolder); + } + + // This has an extra parameter, because otherwise it erases to the same + // as the public one + private WrappedLazy(final IHolder> wrappedHolder, final boolean dummy) { + held = wrappedHolder; + } + + @Override + public IHolder bind(final Function> binder) { + final IHolder> newHolder = held.map((containedHolder) -> { + return containedHolder.bind(binder); + }); + + return new WrappedLazy<>(newHolder, false); + } + + @Override + public Function> lift(final Function func) { + return (val) -> { + return new Lazy<>(func.apply(val)); + }; + } + + @Override + public IHolder map(final Function mapper) { + final IHolder> newHolder = held.map((containedHolder) -> { + return containedHolder.map(mapper); + }); + + return new WrappedLazy<>(newHolder, false); + } + + @Override + public IHolder transform(final UnaryOperator transformer) { + held.transform((containedHolder) -> { + return containedHolder.transform(transformer); + }); + + return this; + } + + @Override + public UnwrappedType unwrap(final Function unwrapper) { + return held.unwrap((containedHolder) -> { + return containedHolder.unwrap(unwrapper); + }); + } +} diff --git a/base/src/main/java/bjc/utils/data/internals/WrappedOption.java b/base/src/main/java/bjc/utils/data/internals/WrappedOption.java new file mode 100644 index 0000000..512c699 --- /dev/null +++ b/base/src/main/java/bjc/utils/data/internals/WrappedOption.java @@ -0,0 +1,76 @@ +package bjc.utils.data.internals; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import bjc.utils.data.IHolder; +import bjc.utils.data.Option; + +public class WrappedOption implements IHolder { + private final IHolder> held; + + public WrappedOption(final IHolder seedValue) { + held = new Option<>(seedValue); + } + + private WrappedOption(final IHolder> toHold, final boolean dummy) { + held = toHold; + } + + @Override + public IHolder bind(final Function> binder) { + final IHolder> newHolder = held.map((containedHolder) -> { + return containedHolder.bind((containedValue) -> { + if (containedValue == null) return new Option<>(null); + + return binder.apply(containedValue); + }); + }); + + return new WrappedOption<>(newHolder, false); + } + + @Override + public Function> lift(final Function func) { + return (val) -> { + return new Option<>(func.apply(val)); + }; + } + + @Override + public IHolder map(final Function mapper) { + final IHolder> newHolder = held.map((containedHolder) -> { + return containedHolder.map((containedValue) -> { + if (containedValue == null) return null; + + return mapper.apply(containedValue); + }); + }); + + return new WrappedOption<>(newHolder, false); + } + + @Override + public IHolder transform(final UnaryOperator transformer) { + held.transform((containedHolder) -> { + return containedHolder.transform((containedValue) -> { + if (containedValue == null) return null; + + return transformer.apply(containedValue); + }); + }); + + return this; + } + + @Override + public UnwrappedType unwrap(final Function unwrapper) { + return held.unwrap((containedHolder) -> { + return containedHolder.unwrap((containedValue) -> { + if (containedValue == null) return null; + + return unwrapper.apply(containedValue); + }); + }); + } +} diff --git a/base/src/main/java/bjc/utils/esodata/AbbrevMap.java b/base/src/main/java/bjc/utils/esodata/AbbrevMap.java new file mode 100644 index 0000000..0d54471 --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/AbbrevMap.java @@ -0,0 +1,227 @@ +package bjc.utils.esodata; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.SetMultimap; + +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IMap; + +/** + * Represents a mapping from a set of strings to a mapping of all unambiguous + * prefixes of their respective strings. + * + * This works the same as Ruby's Abbrev. + * + * @author EVE + * + */ +public class AbbrevMap { + /* + * All of the words we have abbreviations for. + */ + private final Set wrds; + + /* + * Maps abbreviations to their strings. + */ + private IMap abbrevMap; + + /* + * Counts how many times we've seen a substring. + */ + private Set seen; + + /* + * Maps ambiguous abbreviations to the strings they could be. + */ + private SetMultimap ambMap; + + /** + * Create a new abbreviation map. + * + * @param words + * The initial set of words to put in the map. + */ + public AbbrevMap(final String... words) { + wrds = new HashSet<>(Arrays.asList(words)); + + recalculate(); + } + + /** + * Recalculate all the abbreviations in this map. + */ + public void recalculate() { + abbrevMap = new FunctionalMap<>(); + + ambMap = HashMultimap.create(); + + seen = new HashSet<>(); + + for (final String word : wrds) { + /* + * A word always abbreviates to itself. + */ + abbrevMap.put(word, word); + + intAddWord(word); + } + } + + /** + * Adds words to the abbreviation map. + * + * @param words + * The words to add to the abbreviation map. + */ + public void addWords(final String... words) { + wrds.addAll(Arrays.asList(words)); + + for (final String word : words) { + /* + * A word always abbreviates to itself. + */ + abbrevMap.put(word, word); + + intAddWord(word); + } + } + + /* + * Actually add abbreviations of a word. + */ + private void intAddWord(final String word) { + /* + * Skip blank words. + */ + if (word.equals("")) return; + + /* + * Handle each possible abbreviation. + */ + for (int i = word.length(); i > 0; i--) { + final String subword = word.substring(0, i); + + if (seen.contains(subword)) { + /* + * Remove a mapping if its ambiguous and not a + * whole word. + */ + if (abbrevMap.containsKey(subword) && !wrds.contains(subword)) { + final String oldword = abbrevMap.remove(subword); + + ambMap.put(subword, oldword); + ambMap.put(subword, word); + } else if (!wrds.contains(subword)) { + ambMap.put(subword, word); + } + } else { + seen.add(subword); + + abbrevMap.put(subword, word); + } + } + } + + /** + * Removes words from the abbreviation map. + * + * NOTE: There may be inconsistent behavior after removing a word from + * the map. Use {@link AbbrevMap#recalculate()} to fix it if it occurs. + * + * @param words + * The words to remove. + */ + public void removeWords(final String... words) { + wrds.removeAll(Arrays.asList(words)); + + for (final String word : words) { + intRemoveWord(word); + } + } + + /* + * Actually remove a word. + */ + private void intRemoveWord(final String word) { + /* + * Skip blank words. + */ + if (word.equals("")) return; + + /* + * Handle each possible abbreviation. + */ + for (int i = word.length(); i > 0; i--) { + final String subword = word.substring(0, i); + + if (abbrevMap.containsKey(subword)) { + abbrevMap.remove(subword); + } else { + ambMap.remove(subword, word); + + final Set possWords = ambMap.get(subword); + + if (possWords.size() == 0) { + seen.remove(subword); + } else if (possWords.size() == 1) { + final String newWord = possWords.iterator().next(); + + abbrevMap.put(subword, newWord); + ambMap.remove(subword, newWord); + } + } + } + } + + /** + * Convert an abbreviation into all the strings it could abbreviate + * into. + * + * @param abbrev + * The abbreviation to convert. + * + * @return All the expansions for the provided abbreviation. + */ + public String[] deabbrev(final String abbrev) { + if (abbrevMap.containsKey(abbrev)) + return new String[] { abbrevMap.get(abbrev) }; + else return ambMap.get(abbrev).toArray(new String[0]); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (wrds == null ? 0 : wrds.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof AbbrevMap)) return false; + + final AbbrevMap other = (AbbrevMap) obj; + + if (wrds == null) { + if (other.wrds != null) return false; + } else if (!wrds.equals(other.wrds)) return false; + + return true; + } + + @Override + public String toString() { + final String fmt = "AbbrevMap [wrds=%s, abbrevMap=%s, seen=%s, ambMap=%s]"; + + return String.format(fmt, wrds, abbrevMap, seen, ambMap); + } +} diff --git a/base/src/main/java/bjc/utils/esodata/Directory.java b/base/src/main/java/bjc/utils/esodata/Directory.java new file mode 100644 index 0000000..17b70f5 --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/Directory.java @@ -0,0 +1,106 @@ +package bjc.utils.esodata; + +/** + * Represents a hierarchical map. + * + * What's useful about this is that you can hand sub-directories to people and + * be able to ensure that they can't write outside of it. + * + * @param + * The key type of the map. + * @param + * The value type of the map. + */ +public interface Directory { + /** + * Retrieves a given sub-directory. + * + * @param key + * The key to retrieve the sub-directory for. + * + * @return The sub-directory under that name. + * + * @throws IllegalArgumentException + * If the given sub-directory doesn't exist. + */ + Directory getSubdirectory(K key); + + /** + * Check if a given sub-directory exists. + * + * @param key + * The key to look for the sub-directory under. + * + * @return Whether or not a sub-directory of that name exists. + */ + boolean hasSubdirectory(K key); + + /** + * Insert a sub-directory into the dictionary. + * + * @param key + * The name of the new sub-directory + * @param value + * The sub-directory to insert + * + * @return The old sub-directory attached to this key, or null if such a + * sub-directory didn't exist + */ + Directory putSubdirectory(K key, Directory value); + + /** + * Create a new sub-directory. + * + * Will fail if a sub-directory of that name already exists. + * + * @param key + * The name of the new sub-directory. + * + * @return The new sub-directory, or null if one by that name already + * exists. + */ + default Directory newSubdirectory(final K key) { + if (hasSubdirectory(key)) return null; + + final Directory dir = new SimpleDirectory<>(); + + putSubdirectory(key, dir); + + return dir; + } + + /** + * Check if the directory contains a data-item under the given key. + * + * @param key + * The key to check for. + * + * @return Whether or not there is a data item for the given key. + */ + boolean containsKey(K key); + + /** + * Retrieve a given data-item from the directory. + * + * @param key + * The key to retrieve data for. + * + * @return The value for the given key. + * + * @throws IllegalArgumentException + * If no value exists for the given key. + */ + V getKey(K key); + + /** + * Insert a data-item into the directory. + * + * @param key + * The key to insert into. + * @param val + * The value to insert. + * + * @return The old value of key, or null if such a value didn't exist. + */ + V putKey(K key, V val); +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/esodata/DoubleTape.java b/base/src/main/java/bjc/utils/esodata/DoubleTape.java new file mode 100644 index 0000000..5c463c6 --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/DoubleTape.java @@ -0,0 +1,258 @@ +package bjc.utils.esodata; + +/** + * Double-sided tape is essentially two tapes stuck together with a shared + * cursor. + * + * The main way a double-sided tape differs is that it can be flipped, allowing + * access to another set of data. + * + * However, there is only one cursor, and the position of the cursor on one side + * is the inverse of the position on the other side. + * + * When one side is extended, a null will be inserted into the inactive side + * regardless of the auto-extension policy of the tape. The policy will still be + * respected for the active side. + * + * All operations that refer to the tape refer to the currently active side of + * the tape, except for flip. + * + * Flip refers to the entire tape for 'obvious' reasons. + * + * @param + * The element type of the tape. + * @author bjculkin + */ +public class DoubleTape implements Tape { + private Tape front; + private Tape back; + + /** + * Create a new empty double-sided tape that doesn't autoextend. + */ + public DoubleTape() { + this(false); + } + + /** + * Create a new empty double-sided tape that follows the specified + * auto-extension policy. + * + * @param autoExtnd + * Whether or not to auto-extend the tape to the right w/ + * nulls. + */ + public DoubleTape(final boolean autoExtnd) { + front = new SingleTape<>(autoExtnd); + back = new SingleTape<>(autoExtnd); + } + + /** + * Get the item the tape is currently on. + * + * @return The item the tape is on. + */ + @Override + public T item() { + return front.item(); + } + + /** + * Set the item the tape is currently on. + * + * @param itm + * The new value for the tape item. + */ + @Override + public void item(final T itm) { + front.item(itm); + } + + /** + * Get the current number of elements in the tape. + * + * @return The current number of elements in the tape. + */ + @Override + public int size() { + return front.size(); + } + + @Override + public int position() { + return front.position(); + } + + /** + * Insert an element before the current item. + * + * @param itm + * The item to add. + */ + @Override + public void insertBefore(final T itm) { + front.insertBefore(itm); + back.insertAfter(null); + } + + /** + * Insert an element after the current item. + */ + @Override + public void insertAfter(final T itm) { + front.insertAfter(itm); + back.insertBefore(itm); + } + + /** + * Remove the current element. + * + * Also moves the cursor back one step if possible to maintain relative + * position, and removes the corresponding item from the non-active side + * + * @return The removed item from the active side. + */ + @Override + public T remove() { + back.remove(); + + return front.remove(); + } + + /** + * Move the cursor to the left-most position. + */ + @Override + public void first() { + front.first(); + back.last(); + } + + /** + * Move the cursor the right-most position. + */ + @Override + public void last() { + front.last(); + back.first(); + } + + /** + * Move the cursor one space left. + * + * The cursor can't go past zero. + * + * @return True if the cursor was moved left. + */ + @Override + public boolean left() { + return left(1); + } + + /** + * Move the cursor the specified amount left. + * + * The cursor can't go past zero. Attempts to move the cursor by amounts + * that would exceed zero don't move the cursor at all. + * + * @param amt + * The amount to attempt to move the cursor left. + * + * @return True if the cursor was moved left. + */ + @Override + public boolean left(final int amt) { + final boolean succ = front.left(amt); + + if (succ) { + back.right(amt); + } + + return succ; + } + + /** + * Move the cursor one space right. + * + * Moving the cursor right will auto-extend the tape if that is enabled. + * + * @return Whether the cursor was moved right. + */ + @Override + public boolean right() { + return right(1); + } + + /** + * Move the cursor the specified amount right. + * + * Moving the cursor right will auto-extend the tape if that is enabled. + * + * @param amt + * The amount to move the cursor right by. + * + * @return Whether the cursor was moved right. + */ + @Override + public boolean right(final int amt) { + final boolean succ = front.right(amt); + + if (succ) { + back.left(amt); + } + + return succ; + } + + /** + * Flips the tape. + * + * The active side becomes inactive, and the inactive side becomes + * active. + */ + public void flip() { + final Tape tmp = front; + + front = back; + + back = tmp; + } + + @Override + public boolean isDoubleSided() { + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (back == null ? 0 : back.hashCode()); + result = prime * result + (front == null ? 0 : front.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof DoubleTape)) return false; + + final DoubleTape other = (DoubleTape) obj; + + if (back == null) { + if (other.back != null) return false; + } else if (!back.equals(other.back)) return false; + + if (front == null) { + if (other.front != null) return false; + } else if (!front.equals(other.front)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("DoubleTape [front=%s, back=%s]", front, back); + } +} diff --git a/base/src/main/java/bjc/utils/esodata/PushdownMap.java b/base/src/main/java/bjc/utils/esodata/PushdownMap.java new file mode 100644 index 0000000..a631704 --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/PushdownMap.java @@ -0,0 +1,148 @@ +package bjc.utils.esodata; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; + +/** + * A variant of a map where inserting a duplicate key shadows the existing value + * instead of replacing it. + * + * @author EVE + * + * @param + * The key of the map. + * @param + * The values in the map. + */ +public class PushdownMap implements IMap { + private final IMap> backing; + + /** + * Create a new empty stack-based map. + */ + public PushdownMap() { + backing = new FunctionalMap<>(); + } + + private PushdownMap(final IMap> back) { + backing = back; + } + + @Override + public void clear() { + backing.clear(); + } + + @Override + public boolean containsKey(final KeyType key) { + return backing.containsKey(key); + } + + @Override + public IMap extend() { + return new PushdownMap<>(backing.extend()); + } + + @Override + public void forEach(final BiConsumer action) { + backing.forEach((key, stk) -> action.accept(key, stk.top())); + } + + @Override + public void forEachKey(final Consumer action) { + backing.forEachKey(action); + } + + @Override + public void forEachValue(final Consumer action) { + backing.forEachValue(stk -> action.accept(stk.top())); + } + + @Override + public ValueType get(final KeyType key) { + return backing.get(key).top(); + } + + @Override + public int size() { + return backing.size(); + } + + @Override + public IList keyList() { + return backing.keyList(); + } + + @Override + public IMap transform(final Function transformer) { + throw new UnsupportedOperationException("Cannot transform pushdown maps."); + } + + @Override + public ValueType put(final KeyType key, final ValueType val) { + if (backing.containsKey(key)) { + final Stack stk = backing.get(key); + + final ValueType vl = stk.top(); + + stk.push(val); + + return vl; + } else { + final Stack stk = new SimpleStack<>(); + + stk.push(val); + + return null; + } + } + + @Override + public ValueType remove(final KeyType key) { + final Stack stk = backing.get(key); + + if (stk.size() > 1) + return stk.pop(); + else return backing.remove(key).top(); + } + + @Override + public IList valueList() { + return backing.valueList().map(stk -> stk.top()); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (backing == null ? 0 : backing.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof PushdownMap)) return false; + + final PushdownMap other = (PushdownMap) obj; + + if (backing == null) { + if (other.backing != null) return false; + } else if (!backing.equals(other.backing)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("PushdownMap [backing=%s]", backing); + } +} diff --git a/base/src/main/java/bjc/utils/esodata/QueueStack.java b/base/src/main/java/bjc/utils/esodata/QueueStack.java new file mode 100644 index 0000000..850598a --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/QueueStack.java @@ -0,0 +1,88 @@ +package bjc.utils.esodata; + +import java.util.Deque; +import java.util.LinkedList; + +/** + * A FIFO implementation of a stack. + * + * @param + * The datatype stored in the stack. + * @author Ben Culkin + */ +public class QueueStack extends Stack { + private final Deque backing; + + /** + * Create a new empty stack queue. + * + */ + public QueueStack() { + backing = new LinkedList<>(); + } + + @Override + public void push(final T elm) { + backing.add(elm); + } + + @Override + public T pop() { + if (backing.isEmpty()) throw new StackUnderflowException(); + + return backing.remove(); + } + + @Override + public T top() { + if (backing.isEmpty()) throw new StackUnderflowException(); + + return backing.peek(); + } + + @Override + public int size() { + return backing.size(); + } + + @Override + public boolean empty() { + return backing.size() == 0; + } + + @SuppressWarnings("unchecked") + @Override + public T[] toArray() { + return (T[]) backing.toArray(); + } + + @Override + public String toString() { + return String.format("QueueStack [backing=%s]", backing); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + (backing == null ? 0 : backing.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof QueueStack)) return false; + + final QueueStack other = (QueueStack) obj; + + if (backing == null) { + if (other.backing != null) return false; + } else if (!backing.equals(other.backing)) return false; + + return true; + } +} diff --git a/base/src/main/java/bjc/utils/esodata/SimpleDirectory.java b/base/src/main/java/bjc/utils/esodata/SimpleDirectory.java new file mode 100644 index 0000000..69fd019 --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/SimpleDirectory.java @@ -0,0 +1,95 @@ +package bjc.utils.esodata; + +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IMap; + +/** + * Simple implementation of {@link Directory}. + * + * Has a split namespace for data and children. + * + * @author EVE + * + * @param + * The key type of the directory. + * @param + * The value type of the directory. + */ +public class SimpleDirectory implements Directory { + private final IMap> children; + + private final IMap data; + + /** + * Create a new directory. + */ + public SimpleDirectory() { + children = new FunctionalMap<>(); + data = new FunctionalMap<>(); + } + + @Override + public Directory getSubdirectory(final K key) { + return children.get(key); + } + + @Override + public boolean hasSubdirectory(final K key) { + return children.containsKey(key); + } + + @Override + public Directory putSubdirectory(final K key, final Directory val) { + return children.put(key, val); + } + + @Override + public boolean containsKey(final K key) { + return data.containsKey(key); + } + + @Override + public V getKey(final K key) { + return data.get(key); + } + + @Override + public V putKey(final K key, final V val) { + return data.put(key, val); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (children == null ? 0 : children.hashCode()); + result = prime * result + (data == null ? 0 : data.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof SimpleDirectory)) return false; + + final SimpleDirectory other = (SimpleDirectory) obj; + + if (children == null) { + if (other.children != null) return false; + } else if (!children.equals(other.children)) return false; + + if (data == null) { + if (other.data != null) return false; + } else if (!data.equals(other.data)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("SimpleDirectory [children=%s, data=%s]", children, data); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/esodata/SimpleStack.java b/base/src/main/java/bjc/utils/esodata/SimpleStack.java new file mode 100644 index 0000000..fdb3300 --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/SimpleStack.java @@ -0,0 +1,88 @@ +package bjc.utils.esodata; + +import java.util.Deque; +import java.util.LinkedList; + +/** + * Simple implementation of a stack. + * + * @param + * The datatype stored in the stack. + * @author Ben Culkin + */ +public class SimpleStack extends Stack { + private final Deque backing; + + /** + * Create a new empty stack. + * + */ + public SimpleStack() { + backing = new LinkedList<>(); + } + + @Override + public void push(final T elm) { + backing.push(elm); + } + + @Override + public T pop() { + if (backing.isEmpty()) throw new StackUnderflowException(); + + return backing.pop(); + } + + @Override + public T top() { + if (backing.isEmpty()) throw new StackUnderflowException(); + + return backing.peek(); + } + + @Override + public int size() { + return backing.size(); + } + + @Override + public boolean empty() { + return backing.size() == 0; + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray() { + return (T[]) backing.toArray(); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (backing == null ? 0 : backing.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof SimpleStack)) return false; + + final SimpleStack other = (SimpleStack) obj; + + if (backing == null) { + if (other.backing != null) return false; + } else if (!backing.equals(other.backing)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("SimpleStack [backing=%s]", backing); + } +} diff --git a/base/src/main/java/bjc/utils/esodata/SingleTape.java b/base/src/main/java/bjc/utils/esodata/SingleTape.java new file mode 100644 index 0000000..c50be92 --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/SingleTape.java @@ -0,0 +1,255 @@ +package bjc.utils.esodata; + +import java.util.ArrayList; + +/** + * A tape is a one-dimensional array that can only be accessed in one position + * at a time. + * + * A tape is essentially a 1D array with a cursor attached to it, and you can + * only affect elements at that cursor. The size of the array is theoretically + * unbounded to the right, but in practice bounded by available memory. + * + * You can choose whether or not you want the tape to automatically extend + * itself to the right with null elements by specifying its auto-extension + * policy. + * + * @param + * The element type of the tape. + * + * @author bjculkin + */ +public class SingleTape implements Tape { + protected ArrayList backing; + protected int pos; + + protected boolean autoExtend; + + /** + * Create a new tape with the specified contents that doesn't + * autoextend. + */ + public SingleTape(T... vals) { + autoExtend = false; + + backing = new ArrayList(vals.length); + + for(T val : vals) { + backing.add(val); + } + } + /** + * Create a new empty tape that doesn't autoextend. + */ + public SingleTape() { + this(false); + } + + /** + * Create a new empty tape that follows the specified auto-extension + * policy. + * + * @param autoExtnd + * Whether or not to auto-extend the tape to the right w/ + * nulls. + */ + public SingleTape(final boolean autoExtnd) { + autoExtend = autoExtnd; + + backing = new ArrayList<>(); + } + + /** + * Get the item the tape is currently on. + * + * @return The item the tape is on. + */ + @Override + public T item() { + return backing.get(pos); + } + + /** + * Set the item the tape is currently on. + * + * @param itm + * The new value for the tape item. + */ + @Override + public void item(final T itm) { + backing.set(pos, itm); + } + + /** + * Get the current number of elements in the tape. + * + * @return The current number of elements in the tape. + */ + @Override + public int size() { + return backing.size(); + } + + @Override + public int position() { + return pos; + } + + /** + * Insert an element before the current item. + * + * @param itm + * The item to add. + */ + @Override + public void insertBefore(final T itm) { + backing.add(pos, itm); + } + + /** + * Insert an element after the current item. + */ + @Override + public void insertAfter(final T itm) { + if (pos == backing.size() - 1) { + backing.add(itm); + } else { + backing.add(pos + 1, itm); + } + } + + /** + * Remove the current element. + * + * Also moves the cursor back one step if possible to maintain relative + * position. + * + * @return The removed item. + */ + @Override + public T remove() { + final T res = backing.remove(pos); + if (pos != 0) { + pos -= 1; + } + return res; + } + + /** + * Move the cursor to the left-most position. + */ + @Override + public void first() { + pos = 0; + } + + /** + * Move the cursor the right-most position. + */ + @Override + public void last() { + pos = backing.size() - 1; + } + + /** + * Move the cursor one space left. + * + * The cursor can't go past zero. + * + * @return True if the cursor was moved left. + */ + @Override + public boolean left() { + return left(1); + } + + /** + * Move the cursor the specified amount left. + * + * The cursor can't go past zero. Attempts to move the cursor by amounts + * that would exceed zero don't move the cursor at all. + * + * @param amt + * The amount to attempt to move the cursor left. + * + * @return True if the cursor was moved left. + */ + @Override + public boolean left(final int amt) { + if (pos - amt < 0) return false; + + pos -= amt; + return true; + } + + /** + * Move the cursor one space right. + * + * Moving the cursor right will auto-extend the tape if that is enabled. + * + * @return Whether the cursor was moved right. + */ + @Override + public boolean right() { + return right(1); + } + + /** + * Move the cursor the specified amount right. + * + * Moving the cursor right will auto-extend the tape if that is enabled. + * + * @param amt + * The amount to move the cursor right by. + * + * @return Whether the cursor was moved right. + */ + @Override + public boolean right(final int amt) { + if (pos + amt >= backing.size() - 1) { + if (autoExtend) { + while (pos + amt >= backing.size() - 1) { + backing.add(null); + } + } else return false; + } + + pos += amt; + return true; + } + + @Override + public boolean isDoubleSided() { + return false; + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (backing == null ? 0 : backing.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof SingleTape)) return false; + + final SingleTape other = (SingleTape) obj; + + if (backing == null) { + if (other.backing != null) return false; + } else if (!backing.equals(other.backing)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("SingleTape [backing=%s, pos=%s, autoExtend=%s]", backing, pos, autoExtend); + } +} diff --git a/base/src/main/java/bjc/utils/esodata/SpaghettiStack.java b/base/src/main/java/bjc/utils/esodata/SpaghettiStack.java new file mode 100644 index 0000000..7c8c757 --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/SpaghettiStack.java @@ -0,0 +1,99 @@ +package bjc.utils.esodata; + +import java.util.Arrays; +import java.util.stream.Stream; + +/* + * Implements a spaghetti stack, which is a stack that is branched off of a + * parent stack. + * + * @param T The datatype stored in the stack. + * @author Ben Culkin + */ +class SpaghettiStack extends Stack { + private final Stack backing; + + private final Stack parent; + + /** + * Create a new empty spaghetti stack, off of the specified parent. + * + * @param par + * The parent stack + */ + public SpaghettiStack(final Stack par) { + backing = new SimpleStack<>(); + + parent = par; + } + + @Override + public void push(final T elm) { + backing.push(elm); + } + + @Override + public T pop() { + if (backing.empty()) return parent.pop(); + + return backing.pop(); + } + + @Override + public T top() { + if (backing.empty()) return parent.top(); + + return backing.top(); + } + + @Override + public int size() { + return parent.size() + backing.size(); + } + + @Override + public boolean empty() { + return backing.empty() && parent.empty(); + } + + @SuppressWarnings("unchecked") + @Override + public T[] toArray() { + return (T[]) Stream.concat(Arrays.stream(parent.toArray()), Arrays.stream(backing.toArray())).toArray(); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (backing == null ? 0 : backing.hashCode()); + result = prime * result + (parent == null ? 0 : parent.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof SpaghettiStack)) return false; + + final SpaghettiStack other = (SpaghettiStack) obj; + + if (backing == null) { + if (other.backing != null) return false; + } else if (!backing.equals(other.backing)) return false; + + if (parent == null) { + if (other.parent != null) return false; + } else if (!parent.equals(other.parent)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("SpaghettiStack [backing=%s, parent=%s]", backing, parent); + } +} diff --git a/base/src/main/java/bjc/utils/esodata/Stack.java b/base/src/main/java/bjc/utils/esodata/Stack.java new file mode 100644 index 0000000..9d74e9a --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/Stack.java @@ -0,0 +1,459 @@ +package bjc.utils.esodata; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * A stack, with support for combinators. + * + * A FILO stack with support for forth/factor style combinators. + * + *

+ *

Stack underflow

+ *

+ * NOTE: In general, using any operation that attempts to remove more data from + * the stack than exists will cause a {@link StackUnderflowException} to be + * thrown. Check the size of the stack if you want to avoid this. + *

+ *

+ * + * @param + * The datatype stored in the stack. + * + * @author Ben Culkin + */ +public abstract class Stack { + /** + * The exception thrown when attempting to access an element from the + * stack that isn't there. + * + * @author EVE + * + */ + public static class StackUnderflowException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 1423867176204571539L; + } + + /** + * Push an element onto the stack. + * + * @param elm + * The element to insert. + */ + public abstract void push(T elm); + + /** + * Pop an element off of the stack. + * + * @return The element on top of the stack. + */ + public abstract T pop(); + + /** + * Retrieve the top element of this stack without removing it from the + * stack. + * + * @return The top element of this stack. + */ + public abstract T top(); + + /** + * Get the number of elements in the stack. + * + * @return the number of elements in the stack. + */ + public abstract int size(); + + /** + * Check if the stack is empty. + * + * @return Whether or not the stack is empty. + */ + public abstract boolean empty(); + + /** + * Create a spaghetti stack branching off of this one. + * + * @return A spaghetti stack with this stack as a parent. + */ + public Stack spaghettify() { + return new SpaghettiStack<>(this); + } + + /* + * Basic combinators + */ + + /** + * Drop n items from the stack. + * + * @param n + * The number of items to drop. + */ + public void drop(final int n) { + for (int i = 0; i < n; i++) { + pop(); + } + } + + /** + * Drop one item from the stack. + */ + public void drop() { + drop(1); + } + + /** + * Delete n items below the current one. + * + * @param n + * The number of items below the top to delete. + */ + public void nip(final int n) { + final T elm = pop(); + + drop(n); + + push(elm); + } + + /** + * Delete the second element in the stack. + */ + public void nip() { + nip(1); + } + + /** + * Replicate the top n items of the stack m times. + * + * @param n + * The number of items to duplicate. + * @param m + * The number of times to duplicate items. + */ + public void multidup(final int n, final int m) { + final List lst = new ArrayList<>(n); + + for (int i = n; i > 0; i--) { + lst.set(i - 1, pop()); + } + + for (int i = 0; i < m; i++) { + for (final T elm : lst) { + push(elm); + } + } + } + + /** + * Duplicate the top n items of the stack. + * + * @param n + * The number of items to duplicate. + */ + public void dup(final int n) { + multidup(n, 2); + } + + /** + * Duplicate the top item on the stack. + */ + public void dup() { + dup(1); + } + + /** + * Replicate the n elements below the top one m times. + * + * @param n + * The number of items to duplicate. + * @param m + * The number of times to duplicate items. + */ + public void multiover(final int n, final int m) { + final List lst = new ArrayList<>(n); + + final T elm = pop(); + + for (int i = n; i > 0; i--) { + lst.set(i - 1, pop()); + } + + for (final T nelm : lst) { + push(nelm); + } + push(elm); + + for (int i = 1; i < m; i++) { + for (final T nelm : lst) { + push(nelm); + } + } + } + + /** + * Duplicate the n elements below the top one. + * + * @param n + * The number of items to duplicate. + */ + public void over(final int n) { + multiover(n, 2); + } + + /** + * Duplicate the second item in the stack. + */ + public void over() { + over(1); + } + + /** + * Duplicate the third item in the stack. + */ + public void pick() { + final T z = pop(); + final T y = pop(); + final T x = pop(); + + push(x); + push(y); + push(z); + push(x); + } + + /** + * Swap the top two items on the stack. + */ + public void swap() { + final T y = pop(); + final T x = pop(); + + push(y); + push(x); + } + + /** + * Duplicate the second item below the first item. + */ + public void deepdup() { + final T y = pop(); + final T x = pop(); + + push(x); + push(x); + push(y); + } + + /** + * Swap the second and third items in the stack. + */ + public void deepswap() { + final T z = pop(); + final T y = pop(); + final T x = pop(); + + push(y); + push(x); + push(z); + } + + /** + * Rotate the top three items on the stack + */ + public void rot() { + final T z = pop(); + final T y = pop(); + final T x = pop(); + + push(y); + push(z); + push(x); + } + + /** + * Inversely rotate the top three items on the stack + */ + public void invrot() { + final T z = pop(); + final T y = pop(); + final T x = pop(); + + push(z); + push(x); + push(y); + } + + /* + * Dataflow Combinators + */ + /** + * Hides the top n elements on the stack from cons. + * + * @param n + * The number of elements to hide. + * @param cons + * The action to hide the elements from + */ + public void dip(final int n, final Consumer> cons) { + final List elms = new ArrayList<>(n); + + for (int i = n; i > 0; i--) { + elms.set(i - 1, pop()); + } + + cons.accept(this); + + for (final T elm : elms) { + push(elm); + } + } + + /** + * Hide the top element of the stack from cons. + * + * @param cons + * The action to hide the top from + */ + public void dip(final Consumer> cons) { + dip(1, cons); + } + + /** + * Copy the top n elements on the stack, replacing them once cons is + * done. + * + * @param n + * The number of elements to copy. + * @param cons + * The action to execute. + */ + public void keep(final int n, final Consumer> cons) { + dup(n); + dip(n, cons); + } + + /** + * Apply all the actions in conses to the top n elements of the stack. + * + * @param n + * The number of elements to give to cons. + * @param conses + * The actions to execute. + */ + public void multicleave(final int n, final List>> conses) { + final List elms = new ArrayList<>(n); + + for (int i = n; i > 0; i--) { + elms.set(i - 1, pop()); + } + + for (final Consumer> cons : conses) { + for (final T elm : elms) { + push(elm); + } + + cons.accept(this); + } + } + + /** + * Apply all the actions in conses to the top element of the stack. + * + * @param conses + * The actions to execute. + */ + public void cleave(final List>> conses) { + multicleave(1, conses); + } + + /** + * Apply every action in cons to n arguments. + * + * @param n + * The number of parameters each action takes. + * @param conses + * The actions to execute. + */ + public void multispread(final int n, final List>> conses) { + final List> nelms = new ArrayList<>(conses.size()); + + for (int i = conses.size(); i > 0; i--) { + final List elms = new ArrayList<>(n); + + for (int j = n; j > 0; j--) { + elms.set(j, pop()); + } + + nelms.set(i, elms); + } + + int i = 0; + for (final List elms : nelms) { + for (final T elm : elms) { + push(elm); + } + + conses.get(i).accept(this); + i += 1; + } + } + + /** + * Apply the actions in cons to corresponding elements from the stack. + * + * @param conses + * The actions to execute. + */ + public void spread(final List>> conses) { + multispread(1, conses); + } + + /** + * Apply the action in cons to the first m groups of n arguments. + * + * @param n + * The number of arguments cons takes. + * @param m + * The number of time to call cons. + * @param cons + * The action to execute. + */ + public void multiapply(final int n, final int m, final Consumer> cons) { + final List>> conses = new ArrayList<>(m); + + for (int i = 0; i < m; i++) { + conses.add(cons); + } + + multispread(n, conses); + } + + /** + * Apply cons n times to the corresponding elements in the stack. + * + * @param n + * The number of times to execute cons. + * @param cons + * The action to execute. + */ + public void apply(final int n, final Consumer> cons) { + multiapply(1, n, cons); + } + + /* + * Misc. functions + */ + /** + * Get an array representing this stack. + * + * @return The stack as an array. + */ + public abstract T[] toArray(); +} diff --git a/base/src/main/java/bjc/utils/esodata/Tape.java b/base/src/main/java/bjc/utils/esodata/Tape.java new file mode 100644 index 0000000..b6a2c01 --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/Tape.java @@ -0,0 +1,126 @@ +package bjc.utils.esodata; + +/** + * Interface for something that acts like a tape. + * + * A tape is essentially a 1D array with a cursor attached to it, and you can + * only affect elements at that cursor. The size of the array is theoretically + * unbounded to the right, but in practice bounded by available memory. + * + * @param + * The element type of the tape. + * + * @author bjculkin + */ +public interface Tape { + /** + * Get the item the tape is currently on. + * + * @return The item the tape is on. + */ + T item(); + + /** + * Set the item the tape is currently on. + * + * @param itm + * The new value for the tape item. + */ + void item(T itm); + + /** + * Get the current number of elements in the tape. + * + * @return The current number of elements in the tape. + */ + int size(); + + /** + * Get the position of the current item. + * + * @return The position of the current item. + */ + int position(); + + /** + * Insert an element before the current item. + * + * @param itm + * The item to add. + */ + void insertBefore(T itm); + + /** + * Insert an element after the current item. + * + * @param itm + * The item to insert. + */ + void insertAfter(T itm); + + /** + * Remove the current element. + * + * Also moves the cursor back one step if possible to maintain relative + * position. + * + * @return The removed item. + */ + T remove(); + + /** + * Move the cursor to the left-most position. + */ + void first(); + + /** + * Move the cursor the right-most position. + */ + void last(); + + /** + * Move the cursor one space left. + * + * The cursor can't go past zero. + * + * @return True if the cursor was moved left. + */ + boolean left(); + + /** + * Move the cursor the specified amount left. + * + * The cursor can't go past zero. Attempts to move the cursor by amounts + * that would exceed zero don't move the cursor at all. + * + * @param amt + * The amount to attempt to move the cursor left. + * + * @return True if the cursor was moved left. + */ + boolean left(int amt); + + /** + * Move the cursor one space right. + * + * @return Whether the cursor was moved right. + */ + boolean right(); + + /** + * Move the cursor the specified amount right. + * + * @param amt + * The amount to move the cursor right by. + * + * @return Whether the cursor was moved right. + */ + boolean right(int amt); + + /** + * Is this tape double sided? + * + * @return Whether or not this tape is double-sided. + */ + boolean isDoubleSided(); +} diff --git a/base/src/main/java/bjc/utils/esodata/TapeChanger.java b/base/src/main/java/bjc/utils/esodata/TapeChanger.java new file mode 100644 index 0000000..dc885bc --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/TapeChanger.java @@ -0,0 +1,363 @@ +package bjc.utils.esodata; + +/** + * A tape changer is essentially a tape of tapes. + * + * It has a current tape that you can do operations to, but also operations to + * add/remove other tapes. + * + * If there is no tape currently loaded into the changer, all the methods will + * either return null/false. + * + * @param + * The element type of the tapes. + */ +public class TapeChanger implements Tape { + private Tape> tapes; + private Tape currentTape; + + /** + * Create a new empty tape changer. + */ + public TapeChanger() { + tapes = new SingleTape<>(); + } + + /** + * Create a new tape changer with the specified tapes. + * + * @param current + * The tape to mount first. + * @param others + * The tapes to put in this tape changer. + */ + @SafeVarargs + public TapeChanger(final Tape current, final Tape... others) { + this(); + + tapes.insertBefore(current); + + for (final Tape tp : others) { + tapes.insertAfter(tp); + tapes.right(); + } + + tapes.first(); + currentTape = tapes.item(); + } + + /** + * Get the item the tape is currently on. + * + * @return The item the tape is on. + */ + @Override + public T item() { + if (currentTape == null) return null; + + return currentTape.item(); + } + + /** + * Set the item the tape is currently on. + * + * @param itm + * The new value for the tape item. + */ + @Override + public void item(final T itm) { + if (currentTape == null) return; + + currentTape.item(itm); + } + + /** + * Get the current number of elements in the tape. + * + * @return The current number of elements in the tape. + */ + @Override + public int size() { + if (currentTape == null) return 0; + + return currentTape.size(); + } + + @Override + public int position() { + if (currentTape == null) return 0; + + return currentTape.position(); + } + + /** + * Insert an element before the current item. + * + * @param itm + * The item to add. + */ + @Override + public void insertBefore(final T itm) { + if (currentTape == null) return; + + currentTape.insertBefore(itm); + } + + /** + * Insert an element after the current item. + */ + @Override + public void insertAfter(final T itm) { + if (currentTape == null) return; + + currentTape.insertAfter(itm); + } + + /** + * Remove the current element. + * + * Also moves the cursor back one step if possible to maintain relative + * position, and removes the corresponding item from the non-active side + * + * @return The removed item from the active side. + */ + @Override + public T remove() { + if (currentTape == null) return null; + + return currentTape.remove(); + } + + /** + * Move the cursor to the left-most position. + */ + @Override + public void first() { + if (currentTape == null) return; + + currentTape.first(); + } + + /** + * Move the cursor the right-most position. + */ + @Override + public void last() { + if (currentTape == null) return; + + currentTape.last(); + } + + /** + * Move the cursor one space left. + * + * The cursor can't go past zero. + * + * @return True if the cursor was moved left. + */ + @Override + public boolean left() { + return left(1); + } + + /** + * Move the cursor the specified amount left. + * + * The cursor can't go past zero. Attempts to move the cursor by amounts + * that would exceed zero don't move the cursor at all. + * + * @param amt + * The amount to attempt to move the cursor left. + * + * @return True if the cursor was moved left. + */ + @Override + public boolean left(final int amt) { + if (currentTape == null) return false; + + return currentTape.left(amt); + } + + /** + * Move the cursor one space right. + * + * Moving the cursor right will auto-extend the tape if that is enabled. + * + * @return Whether the cursor was moved right. + */ + @Override + public boolean right() { + return right(1); + } + + /** + * Move the cursor the specified amount right. + * + * Moving the cursor right will auto-extend the tape if that is enabled. + * + * @param amt + * The amount to move the cursor right by. + * + * @return Whether the cursor was moved right. + */ + @Override + public boolean right(final int amt) { + if (currentTape == null) return false; + + return currentTape.right(amt); + } + + /** + * Flips the tape. + * + * The active side becomes inactive, and the inactive side becomes + * active. + * + * If the current tape is not double-sided, does nothing. + */ + public void flip() { + if (currentTape == null) return; + + if (currentTape.isDoubleSided()) { + ((DoubleTape) currentTape).flip(); + } + } + + @Override + public boolean isDoubleSided() { + if (currentTape == null) return false; + + return currentTape.isDoubleSided(); + } + + /** + * Check if a tape is currently loaded. + * + * @return Whether or not a tape is loaded. + */ + public boolean isLoaded() { + return currentTape != null; + } + + /** + * Move to the next tape in the changer. + * + * Attempting to load a tape that isn't there won't eject the current + * tape. + * + * @return Whether or not the next tape was loaded. + */ + public boolean nextTape() { + final boolean succ = tapes.right(); + + if (succ) { + currentTape = tapes.item(); + } + + return succ; + } + + /** + * Move to the previous tape in the changer. + * + * Attempting to load a tape that isn't there won't eject the current + * tape. + * + * @return Whether or not the previous tape was loaded. + */ + public boolean prevTape() { + final boolean succ = tapes.left(); + + if (succ) { + currentTape = tapes.item(); + } + + return succ; + } + + /** + * Inserts a tape into the tape changer. + * + * Any currently loaded tape is ejected, and becomes the previous tape. + * + * The specified tape is loaded. + * + * @param tp + * The tape to insert and load. + */ + public void insertTape(final Tape tp) { + tapes.insertAfter(tp); + tapes.right(); + + currentTape = tapes.item(); + } + + /** + * Removes the current tape. + * + * Does nothing if there is not a tape loaded. + * + * Loads the previous tape, if there is one. + * + * @return The removed tape. + */ + public Tape removeTape() { + if (currentTape == null) return null; + + final Tape tp = tapes.remove(); + currentTape = tapes.item(); + + return tp; + } + + /** + * Ejects the current tape. + * + * Does nothing if no tape is loaded. + */ + public void eject() { + currentTape = null; + } + + /** + * Get how many tapes are currently in the changer. + * + * @return How many tapes are currently in the changer. + */ + public int tapeCount() { + return tapes.size(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (currentTape == null ? 0 : currentTape.hashCode()); + result = prime * result + (tapes == null ? 0 : tapes.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof TapeChanger)) return false; + + final TapeChanger other = (TapeChanger) obj; + + if (currentTape == null) { + if (other.currentTape != null) return false; + } else if (!currentTape.equals(other.currentTape)) return false; + + if (tapes == null) { + if (other.tapes != null) return false; + } else if (!tapes.equals(other.tapes)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("TapeChanger [tapes=%s, currentTape='%s']", tapes, currentTape); + } +} diff --git a/base/src/main/java/bjc/utils/esodata/TapeLibrary.java b/base/src/main/java/bjc/utils/esodata/TapeLibrary.java new file mode 100644 index 0000000..2dbc70b --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/TapeLibrary.java @@ -0,0 +1,340 @@ +package bjc.utils.esodata; + +import java.util.HashMap; +import java.util.Map; + +/** + * A tape changer is essentially a map of tapes. + * + * It has a current tape that you can do operations to, but also operations to + * add/remove other tapes. + * + * If there is no tape currently loaded into the changer, all the methods will + * either return null/false. + * + * @param + * The element type of the tapes. + */ +public class TapeLibrary implements Tape { + private final Map> tapes; + private Tape currentTape; + + /** + * Create a new empty tape library. + */ + public TapeLibrary() { + tapes = new HashMap<>(); + } + + /** + * Get the item the tape is currently on. + * + * @return The item the tape is on. + */ + @Override + public T item() { + if (currentTape == null) return null; + + return currentTape.item(); + } + + /** + * Set the item the tape is currently on. + * + * @param itm + * The new value for the tape item. + */ + @Override + public void item(final T itm) { + if (currentTape == null) return; + + currentTape.item(itm); + } + + /** + * Get the current number of elements in the tape. + * + * @return The current number of elements in the tape. + */ + @Override + public int size() { + if (currentTape == null) return 0; + + return currentTape.size(); + } + + @Override + public int position() { + if (currentTape == null) return 0; + + return currentTape.position(); + } + /** + * Insert an element before the current item. + * + * @param itm + * The item to add. + */ + @Override + public void insertBefore(final T itm) { + if (currentTape == null) return; + + currentTape.insertBefore(itm); + } + + /** + * Insert an element after the current item. + */ + @Override + public void insertAfter(final T itm) { + if (currentTape == null) return; + + currentTape.insertAfter(itm); + } + + /** + * Remove the current element. + * + * Also moves the cursor back one step if possible to maintain relative + * position, and removes the corresponding item from the non-active side + * + * @return The removed item from the active side. + */ + @Override + public T remove() { + if (currentTape == null) return null; + + return currentTape.remove(); + } + + /** + * Move the cursor to the left-most position. + */ + @Override + public void first() { + if (currentTape == null) return; + + currentTape.first(); + } + + /** + * Move the cursor the right-most position. + */ + @Override + public void last() { + if (currentTape == null) return; + + currentTape.last(); + } + + /** + * Move the cursor one space left. + * + * The cursor can't go past zero. + * + * @return True if the cursor was moved left. + */ + @Override + public boolean left() { + return left(1); + } + + /** + * Move the cursor the specified amount left. + * + * The cursor can't go past zero. Attempts to move the cursor by amounts + * that would exceed zero don't move the cursor at all. + * + * @param amt + * The amount to attempt to move the cursor left. + * + * @return True if the cursor was moved left. + */ + @Override + public boolean left(final int amt) { + if (currentTape == null) return false; + + return currentTape.left(amt); + } + + /** + * Move the cursor one space right. + * + * Moving the cursor right will auto-extend the tape if that is enabled. + * + * @return Whether the cursor was moved right. + */ + @Override + public boolean right() { + return right(1); + } + + /** + * Move the cursor the specified amount right. + * + * Moving the cursor right will auto-extend the tape if that is enabled. + * + * @param amt + * The amount to move the cursor right by. + * + * @return Whether the cursor was moved right. + */ + @Override + public boolean right(final int amt) { + if (currentTape == null) return false; + + return currentTape.right(amt); + } + + /** + * Flips the tape. + * + * The active side becomes inactive, and the inactive side becomes + * active. + * + * If the current tape is not double-sided, does nothing. + */ + public void flip() { + if (currentTape == null) return; + + if (currentTape.isDoubleSided()) { + ((DoubleTape) currentTape).flip(); + } + } + + @Override + public boolean isDoubleSided() { + if (currentTape == null) return false; + + return currentTape.isDoubleSided(); + } + + /** + * Check if a tape is currently loaded. + * + * @return Whether or not a tape is loaded. + */ + public boolean isLoaded() { + return currentTape != null; + } + + /** + * Move to the specified tape in the library. + * + * Attempting to load a tape that isn't there won't eject the current + * tape. + * + * @param label + * The label of the tape to load. + * + * @return Whether or not the next tape was loaded. + */ + public boolean switchTape(final String label) { + if (tapes.containsKey(label)) { + currentTape = tapes.get(label); + return true; + } + + return false; + } + + /** + * Inserts a tape into the tape library. + * + * Any currently loaded tape is ejected. + * + * The specified tape is loaded. + * + * Adding a duplicate tape will overwrite any existing types. + * + * @param label + * The label of the tape to add. + * + * @param tp + * The tape to insert and load. + */ + public void insertTape(final String label, final Tape tp) { + tapes.put(label, tp); + + currentTape = tp; + } + + /** + * Remove a tape from the library. + * + * Does nothing if there is not a tape of that name loaded. + * + * @param label + * The tape to remove. + * + * @return The removed tape. + */ + public Tape removeTape(final String label) { + return tapes.remove(label); + } + + /** + * Ejects the current tape. + * + * Does nothing if no tape is loaded. + */ + public void eject() { + currentTape = null; + } + + /** + * Get how many tapes are currently in the library. + * + * @return How many tapes are currently in the library. + */ + public int tapeCount() { + return tapes.size(); + } + + /** + * Check if a specific tape is loaded into the library. + * + * @param label + * The tape to check for. + * + * @return Whether or not a tape of that name exists + */ + public boolean hasTape(final String label) { + return tapes.containsKey(label); + } + + @Override + public int hashCode() { + final int prime = 31; + + int result = 1; + result = prime * result + (currentTape == null ? 0 : currentTape.hashCode()); + result = prime * result + (tapes == null ? 0 : tapes.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof TapeLibrary)) return false; + + final TapeLibrary other = (TapeLibrary) obj; + + if (currentTape == null) { + if (other.currentTape != null) return false; + } else if (!currentTape.equals(other.currentTape)) return false; + + if (tapes == null) { + if (other.tapes != null) return false; + } else if (!tapes.equals(other.tapes)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("TapeLibrary [tapes=%s, currentTape='%s']", tapes, currentTape); + } +} diff --git a/base/src/main/java/bjc/utils/esodata/UnifiedDirectory.java b/base/src/main/java/bjc/utils/esodata/UnifiedDirectory.java new file mode 100644 index 0000000..ffb639f --- /dev/null +++ b/base/src/main/java/bjc/utils/esodata/UnifiedDirectory.java @@ -0,0 +1,105 @@ +package bjc.utils.esodata; + +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IMap; + +/** + * Simple implementation of {@link Directory}. + * + * Has a unified namespace for data and children. + * + * @author EVE + * + * @param + * The key type of the directory. + * @param + * The value type of the directory. + */ +public class UnifiedDirectory implements Directory { + private final IMap> children; + + private final IMap data; + + /** + * Create a new directory. + */ + public UnifiedDirectory() { + children = new FunctionalMap<>(); + data = new FunctionalMap<>(); + } + + @Override + public Directory getSubdirectory(final K key) { + return children.get(key); + } + + @Override + public boolean hasSubdirectory(final K key) { + return children.containsKey(key); + } + + @Override + public Directory putSubdirectory(final K key, final Directory val) { + if (data.containsKey(key)) { + final String msg = String.format("Key %s is already used for data", key); + + throw new IllegalArgumentException(msg); + } + + return children.put(key, val); + } + + @Override + public boolean containsKey(final K key) { + return data.containsKey(key); + } + + @Override + public V getKey(final K key) { + return data.get(key); + } + + @Override + public V putKey(final K key, final V val) { + if (children.containsKey(key)) { + final String msg = String.format("Key %s is already used for sub-directories.", key); + + throw new IllegalArgumentException(msg); + } + + return data.put(key, val); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (children == null ? 0 : children.hashCode()); + result = prime * result + (data == null ? 0 : data.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof UnifiedDirectory)) return false; + + final UnifiedDirectory other = (UnifiedDirectory) obj; + + if (children == null) { + if (other.children != null) return false; + } else if (!children.equals(other.children)) return false; + + if (data == null) { + if (other.data != null) return false; + } else if (!data.equals(other.data)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("UnifiedDirectory [children=%s, data=%s]", children, data); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/exceptions/FileNotChosenException.java b/base/src/main/java/bjc/utils/exceptions/FileNotChosenException.java new file mode 100644 index 0000000..6f5a68a --- /dev/null +++ b/base/src/main/java/bjc/utils/exceptions/FileNotChosenException.java @@ -0,0 +1,31 @@ +package bjc.utils.exceptions; + +import java.io.IOException; + +/** + * Represents the user failing to choose a file. + * + * @author ben + * + */ +public class FileNotChosenException extends IOException { + // Version ID for serialization + private static final long serialVersionUID = -8753348705210831096L; + + /** + * Create a new exception + */ + public FileNotChosenException() { + super(); + } + + /** + * Create a new exception with the given cause + * + * @param cause + * The cause of why the exception was thrown + */ + public FileNotChosenException(final String cause) { + super(cause); + } +} diff --git a/base/src/main/java/bjc/utils/exceptions/PragmaFormatException.java b/base/src/main/java/bjc/utils/exceptions/PragmaFormatException.java new file mode 100644 index 0000000..1ad339d --- /dev/null +++ b/base/src/main/java/bjc/utils/exceptions/PragmaFormatException.java @@ -0,0 +1,31 @@ +package bjc.utils.exceptions; + +import java.util.InputMismatchException; + +/** + * The exception to throw whenever a pragma is used with invalid syntax + * + * @author ben + * + */ +public class PragmaFormatException extends InputMismatchException { + // Version ID for serialization + private static final long serialVersionUID = 1288536477368021069L; + + /** + * Create a new exception + */ + public PragmaFormatException() { + super(); + } + + /** + * Create a new exception with the given message + * + * @param message + * The message to explain why the exception was thrown + */ + public PragmaFormatException(final String message) { + super(message); + } +} diff --git a/base/src/main/java/bjc/utils/exceptions/UnknownPragmaException.java b/base/src/main/java/bjc/utils/exceptions/UnknownPragmaException.java new file mode 100644 index 0000000..6fc9113 --- /dev/null +++ b/base/src/main/java/bjc/utils/exceptions/UnknownPragmaException.java @@ -0,0 +1,25 @@ +package bjc.utils.exceptions; + +import java.util.InputMismatchException; + +/** + * Represents a error from encountering a unknown pragma + * + * @author ben + * + */ +public class UnknownPragmaException extends InputMismatchException { + // Version ID for serialization + private static final long serialVersionUID = -4277573484926638662L; + + /** + * Create a new exception with the given cause + * + * @param cause + * The cause for throwing this exception + */ + public UnknownPragmaException(final String cause) { + super(cause); + } + +} diff --git a/base/src/main/java/bjc/utils/funcdata/ExtendedMap.java b/base/src/main/java/bjc/utils/funcdata/ExtendedMap.java new file mode 100644 index 0000000..909c5e9 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/ExtendedMap.java @@ -0,0 +1,127 @@ +package bjc.utils.funcdata; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +import bjc.utils.funcutils.ListUtils; + +class ExtendedMap implements IMap { + private final IMap delegate; + + private final IMap store; + + public ExtendedMap(final IMap delegate, final IMap store) { + this.delegate = delegate; + this.store = store; + } + + @Override + public void clear() { + store.clear(); + } + + @Override + public boolean containsKey(final KeyType key) { + if (store.containsKey(key)) return true; + + return delegate.containsKey(key); + } + + @Override + public IMap extend() { + return new ExtendedMap<>(this, new FunctionalMap<>()); + } + + @Override + public void forEach(final BiConsumer action) { + store.forEach(action); + + delegate.forEach(action); + } + + @Override + public void forEachKey(final Consumer action) { + store.forEachKey(action); + + delegate.forEachKey(action); + } + + @Override + public void forEachValue(final Consumer action) { + store.forEachValue(action); + + delegate.forEachValue(action); + } + + @Override + public ValueType get(final KeyType key) { + if (store.containsKey(key)) return store.get(key); + + return delegate.get(key); + } + + @Override + public int size() { + return store.size() + delegate.size(); + } + + @Override + public IList keyList() { + return ListUtils.mergeLists(store.keyList(), delegate.keyList()); + } + + @Override + public IMap transform(final Function transformer) { + return new TransformedValueMap<>(this, transformer); + } + + @Override + public ValueType put(final KeyType key, final ValueType val) { + return store.put(key, val); + } + + @Override + public ValueType remove(final KeyType key) { + if (!store.containsKey(key)) return delegate.remove(key); + + return store.remove(key); + } + + @Override + public IList valueList() { + return ListUtils.mergeLists(store.valueList(), delegate.valueList()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (delegate == null ? 0 : delegate.hashCode()); + result = prime * result + (store == null ? 0 : store.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof ExtendedMap)) return false; + + final ExtendedMap other = (ExtendedMap) obj; + + if (delegate == null) { + if (other.delegate != null) return false; + } else if (!delegate.equals(other.delegate)) return false; + if (store == null) { + if (other.store != null) return false; + } else if (!store.equals(other.store)) return false; + + return true; + } + + @Override + public String toString() { + return String.format("ExtendedMap [delegate=%s, store=%s]", delegate, store); + } +} diff --git a/base/src/main/java/bjc/utils/funcdata/FunctionalList.java b/base/src/main/java/bjc/utils/funcdata/FunctionalList.java new file mode 100644 index 0000000..55ea7ff --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/FunctionalList.java @@ -0,0 +1,423 @@ +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.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +import bjc.utils.data.IHolder; +import bjc.utils.data.IPair; +import bjc.utils.data.Identity; +import bjc.utils.data.Pair; + +/** + * A wrapper over another list that provides eager functional operations over + * it. + * + * Differs from a stream in every way except for the fact that they both provide + * functional operations. + * + * @author ben + * + * @param + * The type in this list + */ +public class FunctionalList implements Cloneable, IList { + /* + * The list used as a backing store + */ + private final List wrapped; + + /** + * Create a new empty functional list. + */ + public FunctionalList() { + wrapped = new ArrayList<>(); + } + + /** + * Create a new functional list containing the specified items. + * + * Takes O(n) time, where n is the number of items specified + * + * @param items + * The items to put into this functional list. + */ + @SafeVarargs + public FunctionalList(final E... items) { + wrapped = new ArrayList<>(items.length); + + for (final E item : items) { + wrapped.add(item); + } + } + + /** + * Create a new functional list with the specified size. + * + * @param size + * The size of the backing list . + */ + private FunctionalList(final int size) { + wrapped = new ArrayList<>(size); + } + + /** + * Create a new functional list as a wrapper of a existing list. + * + * Takes O(1) time, since it doesn't copy the list. + * + * @param backing + * The list to use as a backing list. + */ + public FunctionalList(final List backing) { + if (backing == null) throw new NullPointerException("Backing list must be non-null"); + + wrapped = backing; + } + + @Override + public boolean add(final E item) { + return wrapped.add(item); + } + + @Override + public boolean allMatch(final Predicate predicate) { + if (predicate == null) throw new NullPointerException("Predicate must be non-null"); + + for (final E item : wrapped) { + if (!predicate.test(item)) + // We've found a non-matching item + return false; + } + + // All of the items matched + return true; + } + + @Override + public boolean anyMatch(final Predicate predicate) { + if (predicate == null) throw new NullPointerException("Predicate must be not null"); + + for (final E item : wrapped) { + if (predicate.test(item)) + // We've found a matching item + return true; + } + + // We didn't find a matching item + return false; + } + + /** + * Clone this list into a new one, and clone the backing list as well + * + * Takes O(n) time, where n is the number of elements in the list + * + * @return A list + */ + @Override + public IList clone() { + final IList cloned = new FunctionalList<>(); + + for (final E element : wrapped) { + cloned.add(element); + } + + return cloned; + } + + @Override + public IList combineWith(final IList rightList, final 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"); + + final IList returned = new FunctionalList<>(); + + // Get the iterator for the other list + final Iterator rightIterator = rightList.toIterable().iterator(); + + for (final Iterator leftIterator = wrapped.iterator(); leftIterator.hasNext() + && rightIterator.hasNext();) { + // Add the transformed items to the result list + final E leftVal = leftIterator.next(); + final T rightVal = rightIterator.next(); + + returned.add(itemCombiner.apply(leftVal, rightVal)); + } + + return returned; + } + + @Override + public boolean contains(final E item) { + // Check if any items in the list match the provided item + return this.anyMatch(item::equals); + } + + @Override + public E first() { + if (wrapped.size() < 1) + throw new NoSuchElementException("Attempted to get first element of empty list"); + + return wrapped.get(0); + } + + @Override + public IList flatMap(final Function> expander) { + if (expander == null) throw new NullPointerException("Expander must not be null"); + + final IList returned = new FunctionalList<>(this.wrapped.size()); + + forEach(element -> { + final IList expandedElement = expander.apply(element); + + if (expandedElement == null) throw new NullPointerException("Expander returned null list"); + + // Add each element to the returned list + expandedElement.forEach(returned::add); + }); + + return returned; + } + + @Override + public void forEach(final Consumer action) { + if (action == null) throw new NullPointerException("Action is null"); + + wrapped.forEach(action); + } + + @Override + public void forEachIndexed(final 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 + final IHolder currentIndex = new Identity<>(0); + + wrapped.forEach((element) -> { + // Call the action with the index and the value + indexedAction.accept(currentIndex.unwrap(index -> index), element); + + // Increment the value + currentIndex.transform((index) -> index + 1); + }); + } + + @Override + public E getByIndex(final int index) { + return wrapped.get(index); + } + + /** + * Get the internal backing list. + * + * @return The backing list this list is based off of. + */ + public List getInternal() { + return wrapped; + } + + @Override + public IList getMatching(final Predicate predicate) { + if (predicate == null) throw new NullPointerException("Predicate must not be null"); + + final IList returned = new FunctionalList<>(); + + wrapped.forEach((element) -> { + if (predicate.test(element)) { + // The item matches, so add it to the returned + // list + returned.add(element); + } + }); + + return returned; + } + + @Override + public int getSize() { + return wrapped.size(); + } + + @Override + public boolean isEmpty() { + return wrapped.isEmpty(); + } + + /* + * Check if a partition has room for another item + */ + private Boolean isPartitionFull(final int numberPerPartition, final IHolder> currentPartition) { + return currentPartition.unwrap((partition) -> partition.getSize() >= numberPerPartition); + } + + @Override + public IList map(final Function elementTransformer) { + if (elementTransformer == null) throw new NullPointerException("Transformer must be not null"); + + final IList returned = new FunctionalList<>(this.wrapped.size()); + + forEach(element -> { + // Add the transformed item to the result + returned.add(elementTransformer.apply(element)); + }); + + return returned; + } + + @Override + public IList> pairWith(final IList rightList) { + return combineWith(rightList, Pair::new); + } + + @Override + public IList> partition(final int numberPerPartition) { + if (numberPerPartition < 1 || numberPerPartition > wrapped.size()) { + final String fmt = "%s is an invalid partition size. Must be between 1 and %d"; + final String msg = String.format(fmt, numberPerPartition, wrapped.size()); + + throw new IllegalArgumentException(msg); + } + + final IList> returned = new FunctionalList<>(); + + // The current partition being filled + final IHolder> currentPartition = new Identity<>(new FunctionalList<>()); + + this.forEach(element -> { + if (isPartitionFull(numberPerPartition, currentPartition)) { + // Add the partition to the list + returned.add(currentPartition.unwrap(partition -> partition)); + + // Start a new partition + currentPartition.transform(partition -> new FunctionalList<>()); + } else { + // Add the element to the current partition + currentPartition.unwrap(partition -> partition.add(element)); + } + }); + + return returned; + } + + @Override + public void prepend(final E item) { + wrapped.add(0, item); + } + + @Override + public E randItem(final Function rnd) { + if (rnd == null) throw new NullPointerException("Random source must not be null"); + + final int randomIndex = rnd.apply(wrapped.size()); + + return wrapped.get(randomIndex); + } + + @Override + public F reduceAux(final T initialValue, final BiFunction stateAccumulator, + final 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 + final IHolder currentState = new Identity<>(initialValue); + + wrapped.forEach(element -> { + // Accumulate a new value into the state + currentState.transform(state -> stateAccumulator.apply(element, state)); + }); + + // Convert the state to its final value + return currentState.unwrap(resultTransformer); + } + + @Override + public boolean removeIf(final Predicate removePredicate) { + if (removePredicate == null) throw new NullPointerException("Predicate must be non-null"); + + return wrapped.removeIf(removePredicate); + } + + @Override + public void removeMatching(final E desiredElement) { + removeIf(element -> element.equals(desiredElement)); + } + + @Override + public void reverse() { + Collections.reverse(wrapped); + } + + @Override + public E search(final E searchKey, final Comparator comparator) { + // Search our internal list + final int foundIndex = Collections.binarySearch(wrapped, searchKey, comparator); + + if (foundIndex >= 0) // We found a matching element + return wrapped.get(foundIndex); + + // We didn't find an element + return null; + } + + @Override + public void sort(final Comparator comparator) { + // sb.deleteCharAt(sb.length() - 2); + Collections.sort(wrapped, comparator); + } + + @Override + public IList tail() { + return new FunctionalList<>(wrapped.subList(1, getSize())); + } + + @Override + public E[] toArray(final E[] arrType) { + return wrapped.toArray(arrType); + } + + @Override + public Iterable toIterable() { + return wrapped; + } + + @Override + public String toString() { + final int lSize = getSize(); + + if (lSize == 0) return "()"; + + final StringBuilder sb = new StringBuilder("("); + final Iterator itr = toIterable().iterator(); + final E itm = itr.next(); + int i = 0; + + if (lSize == 1) return "(" + itm + ")"; + + for (final E item : toIterable()) { + sb.append(item.toString()); + + if (i < lSize - 1) { + sb.append(", "); + } + + i += 1; + } + + sb.append(")"); + + return sb.toString(); + } +} diff --git a/base/src/main/java/bjc/utils/funcdata/FunctionalMap.java b/base/src/main/java/bjc/utils/funcdata/FunctionalMap.java new file mode 100644 index 0000000..c4f0ff1 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/FunctionalMap.java @@ -0,0 +1,175 @@ +package bjc.utils.funcdata; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +import bjc.utils.data.IPair; + +/** + * Basic implementation of {@link IMap} + * + * @author ben + * + * @param + * The type of the map's keys + * @param + * The type of the map's values + */ +public class FunctionalMap implements IMap { + private Map wrappedMap; + + /** + * Create a new blank functional map + */ + public FunctionalMap() { + wrappedMap = new HashMap<>(); + } + + /** + * Create a new functional map with the specified entries + * + * @param entries + * The entries to put into the map + */ + @SafeVarargs + public FunctionalMap(final IPair... entries) { + this(); + + for (final IPair entry : entries) { + entry.doWith((key, val) -> { + wrappedMap.put(key, val); + }); + } + } + + /** + * Create a new functional map wrapping the specified map + * + * @param wrap + * The map to wrap + */ + public FunctionalMap(final Map wrap) { + if (wrap == null) throw new NullPointerException("Map to wrap must not be null"); + + wrappedMap = wrap; + } + + @Override + public void clear() { + wrappedMap.clear(); + } + + @Override + public boolean containsKey(final KeyType key) { + return wrappedMap.containsKey(key); + } + + @Override + public IMap extend() { + return new ExtendedMap<>(this, new FunctionalMap<>()); + } + + @Override + public void forEach(final BiConsumer action) { + wrappedMap.forEach(action); + } + + @Override + public void forEachKey(final Consumer action) { + wrappedMap.keySet().forEach(action); + } + + @Override + public void forEachValue(final Consumer action) { + wrappedMap.values().forEach(action); + } + + @Override + public ValueType get(final KeyType key) { + if (key == null) throw new NullPointerException("Key must not be null"); + + if (!wrappedMap.containsKey(key)) { + final String msg = String.format("Key %s is not present in the map", key); + + throw new IllegalArgumentException(msg); + } + + return wrappedMap.get(key); + } + + @Override + public int size() { + return wrappedMap.size(); + } + + @Override + public IList keyList() { + final FunctionalList keys = new FunctionalList<>(); + + wrappedMap.keySet().forEach(key -> { + keys.add(key); + }); + + return keys; + } + + @Override + public IMap transform(final Function transformer) { + if (transformer == null) throw new NullPointerException("Transformer must not be null"); + + return new TransformedValueMap<>(this, transformer); + } + + @Override + public ValueType put(final KeyType key, final ValueType val) { + if (key == null) throw new NullPointerException("Key must not be null"); + + return wrappedMap.put(key, val); + } + + @Override + public ValueType remove(final KeyType key) { + return wrappedMap.remove(key); + } + + @Override + public String toString() { + return wrappedMap.toString(); + } + + @Override + public IList valueList() { + final FunctionalList values = new FunctionalList<>(); + + wrappedMap.values().forEach(value -> { + values.add(value); + }); + + return values; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (wrappedMap == null ? 0 : wrappedMap.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof FunctionalMap)) return false; + + final FunctionalMap other = (FunctionalMap) obj; + + if (wrappedMap == null) { + if (other.wrappedMap != null) return false; + } else if (!wrappedMap.equals(other.wrappedMap)) return false; + return true; + } +} diff --git a/base/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java b/base/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java new file mode 100644 index 0000000..e068b46 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java @@ -0,0 +1,159 @@ +package bjc.utils.funcdata; + +import java.util.StringTokenizer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * A string tokenizer that exposes a functional interface + * + * @author ben + * + */ +public class FunctionalStringTokenizer { + /** + * Create a new tokenizer from the specified string. + * + * @param strang + * The string to create a tokenizer from. + * @return A new tokenizer that splits the provided string on spaces. + */ + public static FunctionalStringTokenizer fromString(final String strang) { + if (strang == null) throw new NullPointerException("String to tokenize must be non-null"); + + return new FunctionalStringTokenizer(new StringTokenizer(strang, " ")); + } + + /* + * The string tokenizer being driven + */ + private final StringTokenizer input; + + /** + * Create a functional string tokenizer from a given string + * + * @param inp + * The string to tokenize + */ + public FunctionalStringTokenizer(final String inp) { + if (inp == null) throw new NullPointerException("String to tokenize must be non-null"); + + this.input = new StringTokenizer(inp); + } + + /** + * Create a functional string tokenizer from a given string and set of + * separators + * + * @param input + * The string to tokenize + * @param seperators + * The set of separating tokens to use for splitting + */ + public FunctionalStringTokenizer(final String input, final String seperators) { + if (input == 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(input, seperators); + } + + /** + * Create a functional string tokenizer from a non-functional one + * + * @param toWrap + * The non-functional string tokenizer to wrap + */ + public FunctionalStringTokenizer(final StringTokenizer toWrap) { + if (toWrap == null) throw new NullPointerException("Wrapped tokenizer must not be null"); + + this.input = toWrap; + } + + /** + * Execute a provided action for each of the remaining tokens + * + * @param action + * The action to execute for each token + */ + public void forEachToken(final Consumer action) { + if (action == null) throw new NullPointerException("Action must not be null"); + + while (input.hasMoreTokens()) { + action.accept(input.nextToken()); + } + } + + /** + * Get the string tokenizer encapsulated by this tokenizer + * + * @return The encapsulated tokenizer + */ + public StringTokenizer getInternal() { + return input; + } + + /** + * Check if this tokenizer has more tokens + * + * @return Whether or not this tokenizer has more tokens + */ + public boolean hasMoreTokens() { + return input.hasMoreTokens(); + } + + /** + * Return the next token from the tokenizer. + * + * Returns null if no more tokens are available + * + * @return The next token from the tokenizer + */ + public String nextToken() { + if (input.hasMoreTokens()) // Return the next available token + return input.nextToken(); + + // Return no token + return null; + } + + /** + * Convert this tokenizer into a list of strings + * + * @return This tokenizer, converted into a list of strings + */ + public IList toList() { + return toList((final String element) -> element); + } + + /** + * Convert the contents of this tokenizer into a list. Consumes all of + * the input from this tokenizer. + * + * @param + * The type of the converted tokens + * + * @param transformer + * The function to use to convert tokens. + * @return A list containing all of the converted tokens. + */ + public IList toList(final Function transformer) { + if (transformer == null) throw new NullPointerException("Transformer must not be null"); + + final IList returned = new FunctionalList<>(); + + // Add each token to the list after transforming it + forEachToken(token -> { + final E transformedToken = transformer.apply(token); + + returned.add(transformedToken); + }); + + return returned; + } + + @Override + public String toString() { + return String.format("FunctionalStringTokenizer [input=%s]", input); + } +} diff --git a/base/src/main/java/bjc/utils/funcdata/IList.java b/base/src/main/java/bjc/utils/funcdata/IList.java new file mode 100644 index 0000000..28c09d0 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/IList.java @@ -0,0 +1,416 @@ +package bjc.utils.funcdata; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collector; + +import bjc.utils.data.IPair; +import bjc.utils.functypes.ID; + +/** + * A wrapper over another list that provides functional operations over it. + * + * @author ben + * + * @param + * The type in this list + */ +public interface IList extends Iterable { + /** + * Add an item to this list + * + * @param item + * The item to add to this list. + * @return Whether the item was added to the list successfully. + */ + boolean add(ContainedType item); + + /** + * Add all of the elements in the provided list to this list + * + * @param items + * The list of items to add + * @return True if every item was successfully added to the list, false + * otherwise + */ + default boolean addAll(final IList items) { + return items.map(this::add).anyMatch(bl -> bl == false); + } + + /** + * Add all of the elements in the provided array to this list. + * + * @param items + * The array of items to add. + * + * @return True if every item was successfully added to the list, false + * otherwise. + */ + @SuppressWarnings("unchecked") + default boolean addAll(final ContainedType... items) { + boolean succ = true; + + for (final ContainedType item : items) { + final boolean addSucc = add(item); + + succ = succ ? addSucc : false; + } + + return succ; + } + + /** + * Check if all of the elements of this list match the specified + * predicate. + * + * @param matcher + * The predicate to use for checking. + * @return Whether all of the elements of the list match the specified + * predicate. + */ + boolean allMatch(Predicate matcher); + + /** + * Check if any of the elements in this list match the specified list. + * + * @param matcher + * The predicate to use for checking. + * @return Whether any element in the list matches the provided + * predicate. + */ + boolean anyMatch(Predicate matcher); + + /** + * Reduce the contents of this list using a collector + * + * @param + * The intermediate accumulation type + * @param + * The final, reduced type + * @param collector + * The collector to use for reduction + * @return The reduced list + */ + default ReducedType collect( + final Collector collector) { + final BiConsumer accumulator = collector.accumulator(); + + final StateType initial = collector.supplier().get(); + return reduceAux(initial, (value, state) -> { + accumulator.accept(state, value); + + return state; + }, collector.finisher()); + } + + /** + * Combine this list with another one into a new list and merge the + * results. + * + * Works sort of like a combined zip/map over resulting pairs. Does not + * change the underlying list. + * + * NOTE: The returned list will have the length of the shorter of this + * list and the combined one. + * + * @param + * The type of the second list + * @param + * The type of the combined list + * + * @param list + * The list to combine with + * @param combiner + * The function to use for combining element pairs. + * @return A new list containing the merged pairs of lists. + */ + IList combineWith(IList list, + BiFunction combiner); + + /** + * Check if the list contains the specified item + * + * @param item + * The item to see if it is contained + * @return Whether or not the specified item is in the list + */ + boolean contains(ContainedType item); + + /** + * Get the first element in the list + * + * @return The first element in this list. + */ + ContainedType first(); + + /** + * Apply a function to each member of the list, then flatten the + * results. + * + * Does not change the underlying list. + * + * @param + * The type of the flattened list + * + * @param expander + * The function to apply to each member of the list. + * @return A new list containing the flattened results of applying the + * provided function. + */ + IList flatMap(Function> expander); + + /** + * Apply a given action for each member of the list + * + * @param action + * The action to apply to each member of the list. + */ + @Override + void forEach(Consumer action); + + /** + * Apply a given function to each element in the list and its index. + * + * @param action + * The function to apply to each element in the list and + * its index. + */ + void forEachIndexed(BiConsumer action); + + /** + * Retrieve a value in the list by its index. + * + * @param index + * The index to retrieve a value from. + * @return The value at the specified index in the list. + */ + ContainedType getByIndex(int index); + + /** + * Retrieve a list containing all elements matching a predicate + * + * @param predicate + * The predicate to match by + * @return A list containing all elements that match the predicate + */ + IList getMatching(Predicate predicate); + + /** + * Retrieve the size of the wrapped list + * + * @return The size of the wrapped list + */ + int getSize(); + + /** + * Check if this list is empty. + * + * @return Whether or not this list is empty. + */ + boolean isEmpty(); + + /** + * Create a new list by applying the given function to each element in + * the list. + * + * Does not change the underlying list. + * + * @param + * The type of the transformed list + * + * @param transformer + * The function to apply to each element in the list + * @return A new list containing the mapped elements of this list. + */ + IList map(Function transformer); + + /** + * Zip two lists into a list of pairs + * + * @param + * The type of the second list + * + * @param list + * The list to use as the left side of the pair + * @return A list containing pairs of this element and the specified + * list + */ + IList> pairWith(IList list); + + /** + * Partition this list into a list of sublists + * + * @param partitionSize + * The size of elements to put into each one of the + * sublists + * @return A list partitioned into partitions of size nPerPart + */ + IList> partition(int partitionSize); + + /** + * Prepend an item to the list + * + * @param item + * The item to prepend to the list + */ + void prepend(ContainedType item); + + /** + * Prepend an array of items to the list. + * + * @param items + * The items to prepend to the list. + */ + @SuppressWarnings("unchecked") + default void prependAll(final ContainedType... items) { + for (final ContainedType item : items) { + prepend(item); + } + } + + /** + * Select a random item from the list, using a default random number + * generator + * + * @return A random item from the list + */ + default ContainedType randItem() { + return randItem(num -> (int) (Math.random() * num)); + } + + /** + * Select a random item from this list, using the provided random number + * generator. + * + * @param rnd + * The random number generator to use. + * @return A random element from this list. + */ + ContainedType randItem(Function rnd); + + /** + * Reduce this list to a single value, using a accumulative approach. + * + * @param + * The in-between type of the values + * @param + * The final value type + * + * @param initial + * The initial value of the accumulative state. + * @param accumulator + * The function to use to combine a list element with the + * accumulative state. + * @param transformer + * The function to use to convert the accumulative state + * into a final result. + * @return A single value condensed from this list and transformed into + * its final state. + */ + ReducedType reduceAux(StateType initial, + BiFunction accumulator, + Function transformer); + + /** + * Reduce this list to a single value, using a accumulative approach. + * + * @param + * The in-between type of the values. + * + * @param initial + * The initial value of the accumulative state. + * + * @param accumulator + * The function to use to combine a list element with the + * accumulative state. + * + * @return A single value condensed from this list. + */ + default StateType reduceAux(StateType initial, + BiFunction accumulator) { + return reduceAux(initial, accumulator, ID.id()); + } + + /** + * Remove all elements that match a given predicate + * + * @param predicate + * The predicate to use to determine elements to delete + * @return Whether there was anything that satisfied the predicate + */ + boolean removeIf(Predicate predicate); + + /** + * Remove all parameters that match a given parameter + * + * @param element + * The object to remove all matching copies of + */ + void removeMatching(ContainedType element); + + /** + * Reverse the contents of this list in place + */ + void reverse(); + + /** + * Perform a binary search for the specified key using the provided + * means of comparing elements. + * + * Since this IS a binary search, the list must have been sorted before + * hand. + * + * @param key + * The key to search for. + * @param comparator + * 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. + */ + ContainedType search(ContainedType key, Comparator comparator); + + /** + * Sort the elements of this list using the provided way of comparing + * elements. + * + * Does change the underlying list. + * + * @param comparator + * The way to compare elements for sorting. Pass null to + * use E's natural ordering + */ + void sort(Comparator comparator); + + /** + * Get the tail of this list (the list without the first element + * + * @return The list without the first element + */ + IList tail(); + + /** + * Convert this list into an array + * + * @param type + * The type of array to return + * @return The list, as an array + */ + ContainedType[] toArray(ContainedType[] type); + + /** + * Convert the list into a Iterable + * + * @return An iterable view onto the list + */ + Iterable toIterable(); + + @Override + default Iterator iterator() { + return toIterable().iterator(); + } +} diff --git a/base/src/main/java/bjc/utils/funcdata/IMap.java b/base/src/main/java/bjc/utils/funcdata/IMap.java new file mode 100644 index 0000000..0ee7375 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/IMap.java @@ -0,0 +1,188 @@ +package bjc.utils.funcdata; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * 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 interface IMap { + /** + * Execute an action for each entry in the map. + * + * @param action + * the action to execute for each entry in the map. + */ + void forEach(BiConsumer action); + + /** + * Perform an action for each key in the map. + * + * @param action + * The action to perform on each key in the map. + */ + default void forEachKey(final Consumer action) { + forEach((key, val) -> action.accept(key)); + } + + /** + * Perform an action for each value in the map. + * + * @param action + * The action to perform on each value in the map. + */ + default void forEachValue(final Consumer action) { + forEach((key, val) -> action.accept(val)); + } + + /** + * Check if this map contains the specified key. + * + * @param key + * The key to check. + * + * @return Whether or not the map contains the key. + */ + boolean containsKey(KeyType key); + + /** + * Get the value assigned to the given key. + * + * @param key + * The key to look for a value under. + * + * @return The value of the key. + */ + ValueType get(KeyType key); + + /** + * Get a value from the map, and return a default value if the key + * doesn't exist. + * + * @param key + * The key to attempt to retrieve. + * + * @param defaultValue + * The value to return if the key doesn't exist. + * + * @return The value associated with the key, or the default value if + * the key doesn't exist. + */ + default ValueType getOrDefault(final KeyType key, final ValueType defaultValue) { + try { + return get(key); + } catch (final IllegalArgumentException iaex) { + /* + * We don't care about this, because it indicates a key + * is missing. + */ + return defaultValue; + } + } + + /** + * 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. + * + * @throws UnsupportedOperationException + * if the map implementation doesn't support modifying + * the map + */ + ValueType put(KeyType key, ValueType val); + + /** + * Delete all the values in the map. + */ + default void clear() { + keyList().forEach(key -> remove(key)); + } + + /** + * Get the number of entries in this map. + * + * @return The number of entries in this map. + */ + default int size() { + return keyList().getSize(); + } + + /** + * 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. + */ + default IMap transform(final Function transformer) { + return new TransformedValueMap<>(this, transformer); + } + + /** + * Extends this map, creating a new map that will delegate queries to + * the map, but store any added values itself. + * + * @return An extended map. + */ + IMap extend(); + + /** + * Remove the value bound to the key. + * + * @param key + * The key to remove from the map. + * + * @return The previous value for the key in the map, or null if the key + * wasn't in the class. NOTE: Just because you received null, + * doesn't mean the map wasn't changed. It may mean that someone + * put a null value for that key into the map. + */ + ValueType remove(KeyType key); + + /** + * Get a list of all the keys in this map. + * + * @return A list of all the keys in this map. + */ + IList keyList(); + + /** + * Get a list of the values in this map. + * + * @return A list of values in this map. + */ + default IList valueList() { + final IList returns = new FunctionalList<>(); + + for (final KeyType key : keyList()) { + returns.add(get(key)); + } + + return returns; + } +} diff --git a/base/src/main/java/bjc/utils/funcdata/SentryList.java b/base/src/main/java/bjc/utils/funcdata/SentryList.java new file mode 100644 index 0000000..c322743 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/SentryList.java @@ -0,0 +1,41 @@ +package bjc.utils.funcdata; + +import java.util.List; + +/** + * A list that logs when items are inserted into it. + * + * @author bjculkin + * + * @param + * The type of item in the list. + */ +public class SentryList extends FunctionalList { + /** + * Create a new sentry list. + */ + public SentryList() { + super(); + } + + /** + * Create a new sentry list backed by an existing list. + * + * @param backing + * The backing list. + */ + public SentryList(final List backing) { + super(backing); + } + + @Override + public boolean add(final T item) { + final boolean val = super.add(item); + + if (val) { + System.out.println("Added item (" + item + ") to list"); + } + + return val; + } +} diff --git a/base/src/main/java/bjc/utils/funcdata/TransformedValueMap.java b/base/src/main/java/bjc/utils/funcdata/TransformedValueMap.java new file mode 100644 index 0000000..0ca1fdc --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/TransformedValueMap.java @@ -0,0 +1,102 @@ +package bjc.utils.funcdata; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * A map that transforms values from one type to another + * + * @author ben + * + * @param + * The type of the map's keys + * @param + * The type of the map's values + * @param + * The type of the transformed values + */ +final class TransformedValueMap implements IMap { + private final IMap backing; + private final Function transformer; + + public TransformedValueMap(final IMap backingMap, + final Function transform) { + backing = backingMap; + transformer = transform; + } + + @Override + public void clear() { + backing.clear(); + } + + @Override + public boolean containsKey(final OldKey key) { + return backing.containsKey(key); + } + + @Override + public IMap extend() { + return new ExtendedMap<>(this, new FunctionalMap<>()); + } + + @Override + public void forEach(final BiConsumer action) { + backing.forEach((key, value) -> { + action.accept(key, transformer.apply(value)); + }); + } + + @Override + public void forEachKey(final Consumer action) { + backing.forEachKey(action); + } + + @Override + public void forEachValue(final Consumer action) { + backing.forEachValue(value -> { + action.accept(transformer.apply(value)); + }); + } + + @Override + public NewValue get(final OldKey key) { + return transformer.apply(backing.get(key)); + } + + @Override + public int size() { + return backing.size(); + } + + @Override + public IList keyList() { + return backing.keyList(); + } + + @Override + public IMap transform(final Function transform) { + return new TransformedValueMap<>(this, transform); + } + + @Override + public NewValue put(final OldKey key, final NewValue value) { + throw new UnsupportedOperationException("Can't add items to transformed map"); + } + + @Override + public NewValue remove(final OldKey key) { + return transformer.apply(backing.remove(key)); + } + + @Override + public String toString() { + return backing.toString(); + } + + @Override + public IList valueList() { + return backing.valueList().map(transformer); + } +} diff --git a/base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTree.java b/base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTree.java new file mode 100644 index 0000000..8acd477 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTree.java @@ -0,0 +1,221 @@ +package bjc.utils.funcdata.bst; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.function.Predicate; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * A binary search tree, with some mild support for functional traversal. + * + * @author ben + * + * @param + * The data type stored in the node. + */ +public class BinarySearchTree { + /* + * The comparator for use in ordering items + */ + private final Comparator comparator; + + /* + * The current count of elements in the tree + */ + private int elementCount; + + /* + * The root element of the tree + */ + private ITreePart root; + + /** + * Create a new tree using the specified way to compare elements. + * + * @param cmp + * The thing to use for comparing elements + */ + public BinarySearchTree(final Comparator cmp) { + if (cmp == null) throw new NullPointerException("Comparator must not be null"); + + elementCount = 0; + comparator = cmp; + } + + /** + * Add a node to the binary search tree. + * + * @param element + * The data to add to the binary search tree. + */ + public void addNode(final T element) { + elementCount++; + + if (root == null) { + root = new BinarySearchTreeNode<>(element, null, null); + } else { + root.add(element, comparator); + } + } + + /** + * Check if an adjusted pivot falls with the bounds of a list + * + * @param elements + * The list to get bounds from + * @param pivot + * The pivot + * @param pivotAdjustment + * The distance from the pivot + * @return Whether the adjusted pivot is with the list + */ + private boolean adjustedPivotInBounds(final IList elements, final int pivot, final int pivotAdjustment) { + return pivot - pivotAdjustment >= 0 && pivot + pivotAdjustment < elements.getSize(); + } + + /** + * Balance the tree, and remove soft-deleted nodes for free. + * + * Takes O(N) time, but also O(N) space. + */ + public void balance() { + final IList elements = new FunctionalList<>(); + + // Add each element to the list in sorted order + root.forEach(TreeLinearizationMethod.INORDER, element -> elements.add(element)); + + // Clear the tree + root = null; + + // Set up the pivot and adjustment for readding elements + final int pivot = elements.getSize() / 2; + int pivotAdjustment = 0; + + // Add elements until there aren't any left + while (adjustedPivotInBounds(elements, pivot, pivotAdjustment)) { + if (root == null) { + // Create a new root element + root = new BinarySearchTreeNode<>(elements.getByIndex(pivot), null, null); + } else { + // Add the left and right elements in a balanced + // manner + root.add(elements.getByIndex(pivot + pivotAdjustment), comparator); + + root.add(elements.getByIndex(pivot - pivotAdjustment), comparator); + } + + // Increase the distance from the pivot + pivotAdjustment++; + } + + // Add any trailing unbalanced elements + if (pivot - pivotAdjustment >= 0) { + root.add(elements.getByIndex(pivot - pivotAdjustment), comparator); + } else if (pivot + pivotAdjustment < elements.getSize()) { + root.add(elements.getByIndex(pivot + pivotAdjustment), comparator); + } + } + + /** + * Soft-delete a node from the tree. + * + * Soft-deleted nodes stay in the tree until trim()/balance() is + * invoked, and are not included in traversals/finds. + * + * @param element + * The node to delete + */ + public void deleteNode(final T element) { + elementCount--; + + root.delete(element, comparator); + } + + /** + * Get the root of the tree. + * + * @return The root of the tree. + */ + public ITreePart getRoot() { + return root; + } + + /** + * Check if a node is in the tree + * + * @param element + * The node to check the presence of for the tree. + * @return Whether or not the node is in the tree. + */ + public boolean isInTree(final T element) { + return root.contains(element, comparator); + } + + /** + * Traverse the tree in a specified way until the function fails + * + * @param linearizationMethod + * The way to linearize the tree for traversal + * @param traversalPredicate + * The function to use until it fails + */ + public void traverse(final TreeLinearizationMethod linearizationMethod, final 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"); + + root.forEach(linearizationMethod, traversalPredicate); + } + + /** + * Remove all soft-deleted nodes from the tree. + */ + public void trim() { + final List nodes = new ArrayList<>(elementCount); + + // Add all non-soft deleted nodes to the tree in insertion order + traverse(TreeLinearizationMethod.PREORDER, node -> { + nodes.add(node); + return true; + }); + + // Clear the tree + root = null; + + // Add the nodes to the tree in the order they were inserted + nodes.forEach(node -> addNode(node)); + } + + @Override + public String toString() { + return String.format("BinarySearchTree [elementCount=%s, root='%s']", elementCount, root); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + elementCount; + result = prime * result + (root == null ? 0 : root.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof BinarySearchTree)) return false; + + final BinarySearchTree other = (BinarySearchTree) obj; + + if (elementCount != other.elementCount) return false; + if (root == null) { + if (other.root != null) return false; + } else if (!root.equals(other.root)) return false; + + return true; + } +} diff --git a/base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeLeaf.java b/base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeLeaf.java new file mode 100644 index 0000000..8c4f3f0 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeLeaf.java @@ -0,0 +1,119 @@ +package bjc.utils.funcdata.bst; + +import java.util.Comparator; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * A leaf in a tree. + * + * @author ben + * + * @param + * The data stored in the tree. + */ +public class BinarySearchTreeLeaf implements ITreePart { + /** + * The data held in this tree leaf + */ + protected T data; + + /** + * Whether this node is soft-deleted or not + */ + protected boolean isDeleted; + + /** + * Create a new leaf holding the specified data. + * + * @param element + * The data for the leaf to hold. + */ + public BinarySearchTreeLeaf(final T element) { + data = element; + } + + @Override + public void add(final T element, final Comparator comparator) { + throw new IllegalArgumentException("Can't add to a leaf."); + } + + @Override + public E collapse(final Function leafTransformer, final BiFunction branchCollapser) { + if (leafTransformer == null) throw new NullPointerException("Transformer must not be null"); + + return leafTransformer.apply(data); + } + + @Override + public boolean contains(final T element, final Comparator comparator) { + return this.data.equals(element); + } + + @Override + public T data() { + return data; + } + + @Override + public void delete(final T element, final Comparator comparator) { + if (data.equals(element)) { + isDeleted = true; + } + } + + @Override + public boolean directedWalk(final DirectedWalkFunction treeWalker) { + if (treeWalker == null) throw new NullPointerException("Tree walker must not be null"); + + switch (treeWalker.walk(data)) { + case SUCCESS: + return true; + // We don't have any children to care about + case FAILURE: + case LEFT: + case RIGHT: + default: + return false; + } + } + + @Override + public boolean forEach(final TreeLinearizationMethod linearizationMethod, + final Predicate traversalPredicate) { + if (traversalPredicate == null) throw new NullPointerException("Predicate must not be null"); + + return traversalPredicate.test(data); + } + + @Override + public String toString() { + return String.format("BinarySearchTreeLeaf [data='%s', isDeleted=%s]", data, isDeleted); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (data == null ? 0 : data.hashCode()); + result = prime * result + (isDeleted ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof BinarySearchTreeLeaf)) return false; + + final BinarySearchTreeLeaf other = (BinarySearchTreeLeaf) obj; + + if (data == null) { + if (other.data != null) return false; + } else if (!data.equals(other.data)) return false; + if (isDeleted != other.isDeleted) return false; + + return true; + } +} diff --git a/base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeNode.java b/base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeNode.java new file mode 100644 index 0000000..9f45c17 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeNode.java @@ -0,0 +1,287 @@ +package bjc.utils.funcdata.bst; + +import static bjc.utils.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.FAILURE; +import static bjc.utils.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.LEFT; +import static bjc.utils.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.RIGHT; +import static bjc.utils.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.SUCCESS; + +import java.util.Comparator; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * A binary node in a tree. + * + * @author ben + * + * @param + * The data type stored in the tree. + */ +public class BinarySearchTreeNode extends BinarySearchTreeLeaf { + /* + * The left child of this node + */ + private ITreePart left; + + /* + * The right child of this node + */ + private ITreePart right; + + /** + * Create a new node with the specified data and children. + * + * @param element + * The data to store in this node. + * @param lft + * The left child of this node. + * @param rght + * The right child of this node. + */ + public BinarySearchTreeNode(final T element, final ITreePart lft, final ITreePart rght) { + super(element); + this.left = lft; + this.right = rght; + } + + @Override + public void add(final T element, final Comparator comparator) { + if (comparator == null) throw new NullPointerException("Comparator must not be null"); + + switch (comparator.compare(data, element)) { + case -1: + if (left == null) { + left = new BinarySearchTreeNode<>(element, null, null); + } else { + left.add(element, comparator); + } + break; + case 0: + if (isDeleted) { + isDeleted = false; + } else throw new IllegalArgumentException("Can't add duplicate values"); + break; + case 1: + if (right == null) { + right = new BinarySearchTreeNode<>(element, null, null); + } else { + right.add(element, comparator); + } + break; + default: + throw new IllegalStateException("Error: Comparator yielded invalid value"); + } + } + + @Override + public E collapse(final Function nodeCollapser, final BiFunction branchCollapser) { + if (nodeCollapser == null || branchCollapser == null) + throw new NullPointerException("Collapser must not be null"); + + final E collapsedNode = nodeCollapser.apply(data); + + if (left != null) { + final E collapsedLeftBranch = left.collapse(nodeCollapser, branchCollapser); + + if (right != null) { + final E collapsedRightBranch = right.collapse(nodeCollapser, branchCollapser); + + final E collapsedBranches = branchCollapser.apply(collapsedLeftBranch, + collapsedRightBranch); + + return branchCollapser.apply(collapsedNode, collapsedBranches); + } + + return branchCollapser.apply(collapsedNode, collapsedLeftBranch); + } + + if (right != null) { + final E collapsedRightBranch = right.collapse(nodeCollapser, branchCollapser); + + return branchCollapser.apply(collapsedNode, collapsedRightBranch); + } + + return collapsedNode; + } + + @Override + public boolean contains(final T element, final Comparator comparator) { + if (comparator == null) throw new NullPointerException("Comparator must not be null"); + + return directedWalk(currentElement -> { + switch (comparator.compare(element, currentElement)) { + case -1: + return LEFT; + case 0: + return isDeleted ? FAILURE : SUCCESS; + case 1: + return RIGHT; + default: + return FAILURE; + } + }); + } + + @Override + public void delete(final T element, final Comparator comparator) { + if (comparator == null) throw new NullPointerException("Comparator must not be null"); + + directedWalk(currentElement -> { + switch (comparator.compare(data, element)) { + case -1: + return left == null ? FAILURE : LEFT; + case 0: + isDeleted = true; + return FAILURE; + case 1: + return right == null ? FAILURE : RIGHT; + default: + return FAILURE; + } + }); + } + + @Override + public boolean directedWalk(final DirectedWalkFunction treeWalker) { + if (treeWalker == null) throw new NullPointerException("Walker must not be null"); + + switch (treeWalker.walk(data)) { + case SUCCESS: + return true; + case LEFT: + return left.directedWalk(treeWalker); + case RIGHT: + return right.directedWalk(treeWalker); + case FAILURE: + return false; + default: + return false; + } + } + + @Override + public boolean forEach(final TreeLinearizationMethod linearizationMethod, + final 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, traversalPredicate); + case INORDER: + return inorderTraverse(linearizationMethod, traversalPredicate); + case POSTORDER: + return postorderTraverse(linearizationMethod, traversalPredicate); + default: + throw new IllegalArgumentException( + "Passed an incorrect TreeLinearizationMethod " + linearizationMethod + ". WAT"); + } + } + + private boolean inorderTraverse(final TreeLinearizationMethod linearizationMethod, + final Predicate traversalPredicate) { + if (!traverseLeftBranch(linearizationMethod, traversalPredicate)) return false; + + if (!traverseElement(traversalPredicate)) return false; + + if (!traverseRightBranch(linearizationMethod, traversalPredicate)) return false; + + return true; + } + + private boolean postorderTraverse(final TreeLinearizationMethod linearizationMethod, + final Predicate traversalPredicate) { + if (!traverseLeftBranch(linearizationMethod, traversalPredicate)) return false; + + if (!traverseRightBranch(linearizationMethod, traversalPredicate)) return false; + + if (!traverseElement(traversalPredicate)) return false; + + return true; + + } + + private boolean preorderTraverse(final TreeLinearizationMethod linearizationMethod, + final Predicate traversalPredicate) { + if (!traverseElement(traversalPredicate)) return false; + + if (!traverseLeftBranch(linearizationMethod, traversalPredicate)) return false; + + if (!traverseRightBranch(linearizationMethod, traversalPredicate)) return false; + + return true; + } + + private boolean traverseElement(final Predicate traversalPredicate) { + boolean nodeSuccesfullyTraversed; + + if (isDeleted) { + nodeSuccesfullyTraversed = true; + } else { + nodeSuccesfullyTraversed = traversalPredicate.test(data); + } + + return nodeSuccesfullyTraversed; + } + + private boolean traverseLeftBranch(final TreeLinearizationMethod linearizationMethod, + final Predicate traversalPredicate) { + boolean leftSuccesfullyTraversed; + + if (left == null) { + leftSuccesfullyTraversed = true; + } else { + leftSuccesfullyTraversed = left.forEach(linearizationMethod, traversalPredicate); + } + + return leftSuccesfullyTraversed; + } + + private boolean traverseRightBranch(final TreeLinearizationMethod linearizationMethod, + final Predicate traversalPredicate) { + boolean rightSuccesfullyTraversed; + + if (right == null) { + rightSuccesfullyTraversed = true; + } else { + rightSuccesfullyTraversed = right.forEach(linearizationMethod, traversalPredicate); + } + + return rightSuccesfullyTraversed; + } + + @Override + public String toString() { + return String.format("BinarySearchTreeNode [left='%s', right='%s']", left, right); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + (left == null ? 0 : left.hashCode()); + result = prime * result + (right == null ? 0 : right.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (!(obj instanceof BinarySearchTreeNode)) return false; + + final BinarySearchTreeNode other = (BinarySearchTreeNode) obj; + + if (left == null) { + if (other.left != null) return false; + } else if (!left.equals(other.left)) return false; + + if (right == null) { + if (other.right != null) return false; + } else if (!right.equals(other.right)) return false; + + return true; + } +} diff --git a/base/src/main/java/bjc/utils/funcdata/bst/DirectedWalkFunction.java b/base/src/main/java/bjc/utils/funcdata/bst/DirectedWalkFunction.java new file mode 100644 index 0000000..e11524a --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/bst/DirectedWalkFunction.java @@ -0,0 +1,49 @@ +package bjc.utils.funcdata.bst; + +/** + * Represents a function for doing a directed walk of a binary tree. + * + * @author ben + * + * @param + * The type of element stored in the walked tree + */ +@FunctionalInterface +public interface DirectedWalkFunction { + /** + * Represents the results used to direct a walk in a binary tree. + * + * @author ben + * + */ + public enum DirectedWalkResult { + /** + * Specifies that the function has failed. + */ + FAILURE, + /** + * Specifies that the function wants to move left in the tree + * next. + */ + LEFT, + /** + * Specifies that the function wants to move right in the tree + * next. + */ + RIGHT, + /** + * Specifies that the function has succesfully completed + * + */ + SUCCESS + } + + /** + * Perform a directed walk on a node of a tree. + * + * @param element + * The data stored in the node currently being visited + * @return The way the function wants the walk to go next. + */ + public DirectedWalkResult walk(T element); +} diff --git a/base/src/main/java/bjc/utils/funcdata/bst/ITreePart.java b/base/src/main/java/bjc/utils/funcdata/bst/ITreePart.java new file mode 100644 index 0000000..3aa8880 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/bst/ITreePart.java @@ -0,0 +1,96 @@ +package bjc.utils.funcdata.bst; + +import java.util.Comparator; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * A interface for the fundamental things that want to be part of a tree. + * + * @author ben + * + * @param + * The data contained in this part of the tree. + */ +public interface ITreePart { + /** + * Add a element below this tree part somewhere. + * + * @param element + * The element to add below this tree part + * @param comparator + * The thing to use for comparing values to find where to + * insert the tree part. + */ + public void add(T element, Comparator comparator); + + /** + * Collapses this tree part into a single value. Does not change the + * underlying tree. + * + * @param + * The type of the final collapsed value + * + * @param nodeCollapser + * The function to use to transform data into mapped + * form. + * @param branchCollapser + * The function to use to collapse data in mapped form + * into a single value. + * @return A single value from collapsing the tree. + */ + public E collapse(Function nodeCollapser, BiFunction branchCollapser); + + /** + * Check if this tre part or below it contains the specified data item + * + * @param element + * The data item to look for. + * @param comparator + * The comparator to use to search for the data item + * @return Whether or not the given item is contained in this tree part + * or its children. + */ + public boolean contains(T element, Comparator comparator); + + /** + * Get the data associated with this tree part. + * + * @return The data associated with this tree part. + */ + public T data(); + + /** + * Remove the given node from this tree part and any of its children. + * + * @param element + * The data item to remove. + * @param comparator + * The comparator to use to search for the data item. + */ + public void delete(T element, Comparator comparator); + + /** + * Execute a directed walk through the tree. + * + * @param walker + * The function to use to direct the walk through the + * tree. + * @return Whether the directed walk finished successfully. + */ + public boolean directedWalk(DirectedWalkFunction walker); + + /** + * Execute a provided function for each element of tree it succesfully + * completes for + * + * @param linearizationMethod + * The way to linearize the tree for executing + * @param predicate + * The predicate to apply to each element, where it + * returning false terminates traversal early + * @return Whether the traversal finished succesfully + */ + public boolean forEach(TreeLinearizationMethod linearizationMethod, Predicate predicate); +} diff --git a/base/src/main/java/bjc/utils/funcdata/bst/TreeLinearizationMethod.java b/base/src/main/java/bjc/utils/funcdata/bst/TreeLinearizationMethod.java new file mode 100644 index 0000000..0c83867 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/bst/TreeLinearizationMethod.java @@ -0,0 +1,25 @@ +package bjc.utils.funcdata.bst; + +/** + * Represents the ways to linearize a tree for traversal. + * + * @author ben + * + */ +public enum TreeLinearizationMethod { + /** + * Visit the left side of this tree part, the tree part itself, and then + * the right part. + */ + INORDER, + /** + * Visit the left side of this tree part, the right side, and then the + * tree part itself. + */ + POSTORDER, + /** + * Visit the tree part itself, then the left side of tthis tree part and + * then the right part. + */ + PREORDER +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java b/base/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java new file mode 100644 index 0000000..13c1709 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/theory/Bifunctor.java @@ -0,0 +1,139 @@ +package bjc.utils.funcdata.theory; + +import java.util.function.Function; + +/** + * A functor over a pair of heterogeneous types + * + * @author ben + * @param + * The type stored on the 'left' of the pair + * @param + * The type stored on the 'right' of the pair + * + */ +public interface Bifunctor { + /** + * Alias for functor mapping. + * + * @author EVE + * + * @param + * @param + * @param + * @param + */ + public interface BifunctorMap + extends Function, Bifunctor> { + + } + + /** + * Alias for left functor mapping. + * + * @author EVE + * + * @param + * @param + * @param + */ + public interface LeftBifunctorMap + extends BifunctorMap { + + } + + /** + * Alias for right functor mapping. + * + * @author EVE + * + * @param + * @param + * @param + */ + public interface RightBifunctorMap + extends BifunctorMap { + + } + + /** + * Lift a pair of functions to a single function that maps over both + * parts of a pair + * + * @param + * The old left type of the pair + * @param + * The old right type of the pair + * @param + * The new left type of the pair + * @param + * The new right type of the pair + * @param leftFunc + * The function that maps over the left of the pair + * @param rightFunc + * The function that maps over the right of the pair + * @return A function that maps over both parts of the pair + */ + public default BifunctorMap bimap( + final Function leftFunc, final Function rightFunc) { + final BifunctorMap bimappedFunc = (argPair) -> { + final LeftBifunctorMap leftMapper = argPair.fmapLeft(leftFunc); + + final Bifunctor leftMappedFunctor = leftMapper.apply(argPair); + final RightBifunctorMap rightMapper = leftMappedFunctor + .fmapRight(rightFunc); + + return rightMapper.apply(leftMappedFunctor); + }; + + return bimappedFunc; + } + + /** + * Lift a function to operate over the left part of this pair + * + * @param + * The old left type of the pair + * @param + * The old right type of the pair + * @param + * The new left type of the pair + * @param func + * The function to lift to work over the left side of the + * pair + * @return The function lifted to work over the left side of bifunctors + */ + public LeftBifunctorMap fmapLeft( + Function func); + + /** + * Lift a function to operate over the right part of this pair + * + * @param + * The old left type of the pair + * @param + * The old right type of the pair + * @param + * The new right type of the pair + * @param func + * The function to lift to work over the right side of + * the pair + * @return The function lifted to work over the right side of bifunctors + */ + public RightBifunctorMap fmapRight( + Function func); + + /** + * Get the value contained on the left of this bifunctor + * + * @return The value on the left side of this bifunctor + */ + public LeftType getLeft(); + + /** + * Get the value contained on the right of this bifunctor + * + * @return The value on the right of this bifunctor + */ + public RightType getRight(); +} diff --git a/base/src/main/java/bjc/utils/funcdata/theory/Functor.java b/base/src/main/java/bjc/utils/funcdata/theory/Functor.java new file mode 100644 index 0000000..1c53284 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcdata/theory/Functor.java @@ -0,0 +1,39 @@ +package bjc.utils.funcdata.theory; + +import java.util.function.Function; + +/** + * Represents a container or context some sort usually, but the precise + * definition is that it represents exactly what it is defined as + * + * @author ben + * @param + * The value inside the functor + */ +public interface Functor { + /** + * Converts a normal function to operate over values in a functor. + * + * N.B: Even though the type signature implies that you can apply the + * resulting function to any type of functor, it is only safe to call it + * on instances of the type of functor you called fmap on. + * + * @param + * The argument of the function + * @param + * The return type of the function + * @param func + * The function to convert + * @return The passed in function converted to work over a particular + * type of functors + */ + public Function, Functor> fmap( + Function func); + + /** + * Retrieve the thing inside this functor + * + * @return The thing inside this functor + */ + public ContainedType getValue(); +} diff --git a/base/src/main/java/bjc/utils/functypes/ID.java b/base/src/main/java/bjc/utils/functypes/ID.java new file mode 100644 index 0000000..d3197e2 --- /dev/null +++ b/base/src/main/java/bjc/utils/functypes/ID.java @@ -0,0 +1,20 @@ +package bjc.utils.functypes; + +import java.util.function.UnaryOperator; + +/** + * Identity function. + * + * @author bjculkin + * + */ +public class ID { + /** + * Create an identity function. + * + * @return A identity function. + */ + public static UnaryOperator id() { + return (x) -> x; + } +} diff --git a/base/src/main/java/bjc/utils/functypes/ListFlattener.java b/base/src/main/java/bjc/utils/functypes/ListFlattener.java new file mode 100644 index 0000000..cfa0c8b --- /dev/null +++ b/base/src/main/java/bjc/utils/functypes/ListFlattener.java @@ -0,0 +1,17 @@ +package bjc.utils.functypes; + +import java.util.function.Function; + +import bjc.utils.funcdata.IList; + +/** + * A function that flattens a list. + * + * @author bjculkin + * + * @param + * The type of value in the list. + */ +public interface ListFlattener extends Function, S> { + +} diff --git a/base/src/main/java/bjc/utils/funcutils/CollectorUtils.java b/base/src/main/java/bjc/utils/funcutils/CollectorUtils.java new file mode 100644 index 0000000..a044bfd --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/CollectorUtils.java @@ -0,0 +1,39 @@ +package bjc.utils.funcutils; + +import java.util.stream.Collector; + +import bjc.utils.data.IHolder; +import bjc.utils.data.IPair; + +/** + * Utilities for producing implementations of {@link Collector} + * + * @author ben + * + */ +public class CollectorUtils { + /** + * Create a collector that applies two collectors at once + * + * @param + * The type of the collection to collect from + * @param + * The intermediate type of the first collector + * @param + * The intermediate type of the second collector + * @param + * The final type of the first collector + * @param + * The final type of the second collector + * @param first + * The first collector to use + * @param second + * The second collector to use + * @return A collector that functions as mentioned above + */ + public static Collector>, IPair> compoundCollect( + final Collector first, + final Collector second) { + return new CompoundCollector<>(first, second); + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/CompoundCollector.java b/base/src/main/java/bjc/utils/funcutils/CompoundCollector.java new file mode 100644 index 0000000..35695bc --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/CompoundCollector.java @@ -0,0 +1,89 @@ +package bjc.utils.funcutils; + +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; + +import bjc.utils.data.IHolder; +import bjc.utils.data.IPair; +import bjc.utils.data.Identity; +import bjc.utils.data.Pair; + +final class CompoundCollector + implements Collector>, IPair> { + + private final Set characteristicSet; + + private final Collector first; + private final Collector second; + + public CompoundCollector(final Collector first, + final Collector second) { + this.first = first; + this.second = second; + + characteristicSet = first.characteristics(); + characteristicSet.addAll(second.characteristics()); + } + + @Override + public BiConsumer>, InitialType> accumulator() { + final BiConsumer firstAccumulator = first.accumulator(); + final BiConsumer secondAccumulator = second.accumulator(); + + return (state, value) -> { + state.doWith(statePair -> { + statePair.doWith((left, right) -> { + firstAccumulator.accept(left, value); + secondAccumulator.accept(right, value); + }); + }); + }; + } + + @Override + public Set characteristics() { + return characteristicSet; + } + + @Override + public BinaryOperator>> combiner() { + final BinaryOperator firstCombiner = first.combiner(); + final BinaryOperator secondCombiner = second.combiner(); + + return (leftState, rightState) -> { + return leftState.unwrap(leftPair -> { + return rightState.transform(rightPair -> { + return leftPair.combine(rightPair, firstCombiner, secondCombiner); + }); + }); + }; + } + + @Override + public Function>, IPair> finisher() { + return state -> { + return state.unwrap(pair -> { + return pair.bind((left, right) -> { + final FinalType1 finalLeft = first.finisher().apply(left); + final FinalType2 finalRight = second.finisher().apply(right); + + return new Pair<>(finalLeft, finalRight); + }); + }); + }; + } + + @Override + public Supplier>> supplier() { + return () -> { + final AuxType1 initialLeft = first.supplier().get(); + final AuxType2 initialRight = second.supplier().get(); + + return new Identity<>(new Pair<>(initialLeft, initialRight)); + }; + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/EnumUtils.java b/base/src/main/java/bjc/utils/funcutils/EnumUtils.java new file mode 100644 index 0000000..e4c0bda --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/EnumUtils.java @@ -0,0 +1,63 @@ +package bjc.utils.funcutils; + +import java.util.Random; +import java.util.function.Consumer; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * Utility methods on enums + * + * @author ben + * + */ +public class EnumUtils { + /** + * Do an action for a random number of enum values + * + * @param + * The type of the enum + * @param clasz + * The enum class + * @param nValues + * The number of values to execute the action on + * @param action + * The action to perform on random values + * @param rnd + * The source of randomness to use + */ + public static > void doForValues(final Class clasz, final int nValues, + final Consumer action, final Random rnd) { + final E[] enumValues = clasz.getEnumConstants(); + + final IList valueList = new FunctionalList<>(enumValues); + + final int randomValueCount = enumValues.length - nValues; + + for (int i = 0; i <= randomValueCount; i++) { + final E rDir = valueList.randItem(rnd::nextInt); + + valueList.removeMatching(rDir); + } + + valueList.forEach(action); + } + + /** + * Get a random value from an enum + * + * @param + * The type of the enum + * @param clasz + * The class of the enum + * @param rnd + * The random source to use + * @return A random value from the specified enum + */ + public static > E getRandomValue(final Class clasz, final Random rnd) { + final E[] enumValues = clasz.getEnumConstants(); + + return new FunctionalList<>(enumValues).randItem(rnd::nextInt); + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/FileUtils.java b/base/src/main/java/bjc/utils/funcutils/FileUtils.java new file mode 100644 index 0000000..87199b1 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/FileUtils.java @@ -0,0 +1,40 @@ +package bjc.utils.funcutils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.function.BiPredicate; + +/** + * Utilities for doing things with files + * + * @author ben + * + */ +public class FileUtils { + /** + * Traverse a directory recursively. This is a depth-first traversal + * + * + * @param root + * The directory to start the traversal at + * @param predicate + * The predicate to determine whether or not to traverse + * a directory + * @param action + * The action to invoke upon each file in the directory. + * Returning true means to continue the traversal, + * returning false stops it + * @throws IOException + * if the walk throws an exception + * + * TODO If it becomes necessary, write another overload + * for this with all the buttons and knobs from + * walkFileTree + */ + public static void traverseDirectory(final Path root, final BiPredicate predicate, + final BiPredicate action) throws IOException { + Files.walkFileTree(root, new FunctionalFileVisitor(predicate, action)); + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/FuncUtils.java b/base/src/main/java/bjc/utils/funcutils/FuncUtils.java new file mode 100644 index 0000000..9950add --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/FuncUtils.java @@ -0,0 +1,76 @@ +package bjc.utils.funcutils; + +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +/** + * Utility things for functions + * + * @author ben + * + */ +public class FuncUtils { + /** + * Convert a binary function into a unary function that returns a + * function + * + * @param + * The initial type of the function + * @param + * The intermediate type of the function + * @param + * The terminal type of the function + * @param func + * The function to transform + * @return The function transformed into a unary function returning a + * function + */ + public static Function> curry2(final BiFunction func) { + return arg1 -> arg2 -> { + return func.apply(arg1, arg2); + }; + } + + /** + * Do the specified action the specified number of times + * + * @param nTimes + * The number of times to do the action + * @param cons + * The action to perform + */ + public static void doTimes(final int nTimes, final Consumer cons) { + for (int i = 0; i < nTimes; i++) { + cons.accept(i); + } + } + + /** + * Return an operator that executes until it converges. + * + * @param op + * The operator to execute. + * @param maxTries + * The maximum amount of times to apply the function in an + * attempt to cause it to converge. + */ + public static UnaryOperator converge(final UnaryOperator op, final int maxTries) { + return (val) -> { + T newVal = op.apply(val); + T oldVal; + + int tries = 0; + + do { + oldVal = newVal; + newVal = op.apply(newVal); + + tries += 1; + } while(!newVal.equals(oldVal) && tries < maxTries); + + return newVal; + }; + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java b/base/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java new file mode 100644 index 0000000..db6c43b --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/FunctionalFileVisitor.java @@ -0,0 +1,36 @@ +package bjc.utils.funcutils; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.function.BiPredicate; + +/* + * Functional implementation of a file visitor. + */ +final class FunctionalFileVisitor extends SimpleFileVisitor { + private final BiPredicate predicate; + private final BiPredicate action; + + public FunctionalFileVisitor(final BiPredicate predicate, + final BiPredicate action) { + this.predicate = predicate; + this.action = action; + } + + @Override + public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException { + if (predicate.test(dir, attrs)) return FileVisitResult.CONTINUE; + + return FileVisitResult.SKIP_SUBTREE; + } + + @Override + public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { + if (action.test(file, attrs)) return FileVisitResult.CONTINUE; + + return FileVisitResult.TERMINATE; + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/GroupPartIteration.java b/base/src/main/java/bjc/utils/funcutils/GroupPartIteration.java new file mode 100644 index 0000000..f3b2254 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/GroupPartIteration.java @@ -0,0 +1,62 @@ +package bjc.utils.funcutils; + +import java.util.function.Consumer; +import java.util.function.Function; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * Implements a single group partitioning pass on a list + * + * @author ben + * + * @param + * The type of element in the list being partitioned + */ +final class GroupPartIteration implements Consumer { + private final IList> returnedList; + + public IList currentPartition; + private final IList rejectedItems; + + private int numberInCurrentPartition; + private final int numberPerPartition; + + private final Function elementCounter; + + public GroupPartIteration(final IList> returned, final IList rejects, final int nPerPart, + final Function eleCount) { + this.returnedList = returned; + this.rejectedItems = rejects; + this.numberPerPartition = nPerPart; + this.elementCounter = eleCount; + + this.currentPartition = new FunctionalList<>(); + this.numberInCurrentPartition = 0; + } + + @Override + public void accept(final E value) { + final boolean shouldStartPartition = numberInCurrentPartition >= numberPerPartition; + + if (shouldStartPartition) { + returnedList.add(currentPartition); + + currentPartition = new FunctionalList<>(); + numberInCurrentPartition = 0; + } else { + final int currentElementCount = elementCounter.apply(value); + + final boolean shouldReject = numberInCurrentPartition + + currentElementCount >= numberPerPartition; + + if (shouldReject) { + rejectedItems.add(value); + } else { + currentPartition.add(value); + numberInCurrentPartition += currentElementCount; + } + } + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/IBuilder.java b/base/src/main/java/bjc/utils/funcutils/IBuilder.java new file mode 100644 index 0000000..a96a4d6 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/IBuilder.java @@ -0,0 +1,31 @@ +package bjc.utils.funcutils; + +/** + * Generic interface for objects that implement the builder pattern + * + * @author ben + * + * @param + * The type of object being built + */ +public interface IBuilder { + /** + * Build the object this builder is building + * + * @return The built object + * @throws IllegalStateException + * if the data in the builder cannot be built into its + * corresponding object at this point in time + */ + public E build(); + + /** + * Reset the state of this builder to its initial state + * + * @throws UnsupportedOperationException + * if the builder doesn't support resetting its state + */ + public default void reset() { + throw new UnsupportedOperationException("Builder doesn't support state resetting"); + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/Isomorphism.java b/base/src/main/java/bjc/utils/funcutils/Isomorphism.java new file mode 100644 index 0000000..2d3655e --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/Isomorphism.java @@ -0,0 +1,60 @@ +package bjc.utils.funcutils; + +import java.util.function.Function; + +/** + * A pair of functions to transform between a pair of types. + * + * @author bjculkin + * + * @param + * The source type of the isomorphism. + * + * @param + * The destination type of isomorphism. + * + */ +public class Isomorphism { + private Function toFunc; + private Function fromFunc; + + /** + * Create a new isomorphism. + * + * @param to + * The 'forward' function, from the source to the + * definition. + * + * @param from + * The 'backward' function, from the definition to the + * source. + */ + public Isomorphism(Function to, Function from) { + toFunc = to; + fromFunc = from; + } + + /** + * Apply the isomorphism forward. + * + * @param val + * The source value. + * + * @return The destination value. + */ + public D to(S val) { + return toFunc.apply(val); + } + + /** + * Apply the isomorphism backward. + * + * @param val + * The destination value. + * + * @return The source value. + */ + public S from(D val) { + return fromFunc.apply(val); + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/LambdaLock.java b/base/src/main/java/bjc/utils/funcutils/LambdaLock.java new file mode 100644 index 0000000..62c5d32 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/LambdaLock.java @@ -0,0 +1,105 @@ +package bjc.utils.funcutils; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Supplier; + +/** + * A wrapper around a {@link ReadWriteLock} to ensure that the lock is used + * properly. + * + * @author EVE + * + */ +public class LambdaLock { + private final Lock readLock; + private final Lock writeLock; + + /** + * Create a new lambda-enabled lock around a new lock. + */ + public LambdaLock() { + this(new ReentrantReadWriteLock()); + } + + /** + * Create a new lambda-enabled lock. + * + * @param lck + * The lock to wrap. + */ + public LambdaLock(final ReadWriteLock lck) { + readLock = lck.readLock(); + writeLock = lck.writeLock(); + } + + /** + * Execute an action with the read lock taken. + * + * @param supp + * The action to call. + * + * @return The result of the action. + */ + public T read(final Supplier supp) { + readLock.lock(); + + try { + return supp.get(); + } finally { + readLock.unlock(); + } + } + + /** + * Execute an action with the write lock taken. + * + * @param supp + * The action to call. + * + * @return The result of the action. + */ + public T write(final Supplier supp) { + writeLock.lock(); + + try { + return supp.get(); + } finally { + writeLock.unlock(); + } + } + + /** + * Execute an action with the read lock taken. + * + * @param action + * The action to call. + * + */ + public void read(final Runnable action) { + readLock.lock(); + + try { + action.run(); + } finally { + readLock.unlock(); + } + } + + /** + * Execute an action with the write lock taken. + * + * @param action + * The action to call. + */ + public void write(final Runnable action) { + writeLock.lock(); + + try { + action.run(); + } finally { + writeLock.unlock(); + } + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/funcutils/ListUtils.java b/base/src/main/java/bjc/utils/funcutils/ListUtils.java new file mode 100644 index 0000000..c0daa1e --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/ListUtils.java @@ -0,0 +1,294 @@ +package bjc.utils.funcutils; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.function.Function; +import java.util.function.Supplier; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * Utilities for manipulating FunctionalLists that don't belong in the class + * itself + * + * @author ben + * + */ +public class ListUtils { + private static final int MAX_NTRIESPART = 50; + + /** + * Collapse a string of tokens into a single string without adding any + * spaces + * + * @param input + * The list of tokens to collapse + * @return The collapsed string of tokens + */ + public static String collapseTokens(final IList input) { + if (input == null) throw new NullPointerException("Input must not be null"); + + return collapseTokens(input, ""); + } + + /** + * Collapse a string of tokens into a single string, adding the desired + * separator after each token + * + * @param input + * The list of tokens to collapse + * @param seperator + * The separator to use for separating tokens + * @return The collapsed string of tokens + */ + public static String collapseTokens(final IList input, final 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 { + final StringBuilder state = new StringBuilder(); + + int i = 1; + for (final String itm : input.toIterable()) { + state.append(itm); + + if (i != input.getSize()) { + state.append(seperator); + } + + i += 1; + } + + return state.toString(); + } + } + + /** + * Select a number of random items from the list without replacement + * + * @param + * The type of items to select + * @param list + * The list to select from + * @param number + * The number of items to selet + * @param rng + * A function that creates a random number from 0 to the + * desired number + * @return A new list containing the desired number of items randomly + * selected from the specified list without replacement + */ + + public static IList drawWithoutReplacement(final IList list, final int number, + final Function rng) { + final IList selected = new FunctionalList<>(new ArrayList<>(number)); + + final int total = list.getSize(); + + final Iterator itr = list.toIterable().iterator(); + E element = null; + + for (final int index = 0; itr.hasNext(); element = itr.next()) { + /* + * n - m + */ + final int winningChance = number - selected.getSize(); + + /* + * N - t + */ + final int totalChance = total - (index - 1); + + /* + * Probability of selecting the t+1'th element + */ + if (NumberUtils.isProbable(winningChance, totalChance, rng)) { + selected.add(element); + } + } + + return selected; + } + + /** + * Select a number of random items from the list, with replacement + * + * @param + * The type of items to select + * @param list + * The list to select from + * @param number + * The number of items to selet + * @param rng + * A function that creates a random number from 0 to the + * desired number + * @return A new list containing the desired number of items randomly + * selected from the specified list + */ + public static IList drawWithReplacement(final IList list, final int number, + final Function rng) { + final IList selected = new FunctionalList<>(new ArrayList<>(number)); + + for (int i = 0; i < number; i++) { + selected.add(list.randItem(rng)); + } + + return selected; + } + + /** + * Partition a list into a list of lists, where each element can count + * for more than one element in a partition + * + * @param + * The type of elements in the list to partition + * + * @param input + * The list to partition + * @param counter + * The function to determine the count for each element + * for + * @param partitionSize + * The number of elements to put in each partition + * + * @return A list partitioned according to the above rules + */ + public static IList> groupPartition(final IList input, final Function counter, + final int partitionSize) { + if (input == null) + throw new NullPointerException("Input list must not be null"); + else if (counter == null) + throw new NullPointerException("Counter must not be null"); + else if (partitionSize < 1 || partitionSize > input.getSize()) { + final String fmt = "%d is not a valid partition size. Must be between 1 and %d"; + final String msg = String.format(fmt, partitionSize, input.getSize()); + + throw new IllegalArgumentException(msg); + } + + /* + * List that holds our results + */ + final IList> returned = new FunctionalList<>(); + + /* + * List that holds elements rejected during current pass + */ + final IList rejected = new FunctionalList<>(); + + final GroupPartIteration it = new GroupPartIteration<>(returned, rejected, partitionSize, counter); + + /* + * Run up to a certain number of passes + */ + for (int numberOfIterations = 0; numberOfIterations < MAX_NTRIESPART + && !rejected.isEmpty(); numberOfIterations++) { + input.forEach(it); + + if (rejected.isEmpty()) { + /* + * Nothing was rejected, so we're done + */ + return returned; + } + } + + + final String fmt = "Heuristic (more than %d iterations of partitioning) detected an unpartitionable list. (%s)\nThe following elements were not partitioned: %s\nCurrent group in formation: %s\nPreviously formed groups: %s\n"; + + final String msg = String.format(fmt, MAX_NTRIESPART, input.toString(), rejected.toString(), it.currentPartition.toString(), returned.toString()); + + throw new IllegalArgumentException(msg); + } + + /** + * Merge the contents of a bunch of lists together into a single list + * + * @param + * The type of value in this lists + * @param lists + * The values in the lists to merge + * @return A list containing all the elements of the lists + */ + @SafeVarargs + public static IList mergeLists(final IList... lists) { + final IList returned = new FunctionalList<>(); + + for (final IList list : lists) { + for (final E itm : list.toIterable()) { + returned.add(itm); + } + } + + return returned; + } + + /** + * Pad the provided list out to the desired size + * + * @param + * The type of elements in the list + * @param list + * The list to pad out + * @param counter + * The function to count elements with + * @param size + * The desired size of the list + * @param padder + * The function to get elements to pad with + * @return The list, padded to the desired size + * @throws IllegalArgumentException + * if the list couldn't be padded to the desired size + */ + public static IList padList(final IList list, final Function counter, final int size, + final Supplier padder) { + int count = 0; + + final IList returned = new FunctionalList<>(); + + for (final E itm : list.toIterable()) { + count += counter.apply(itm); + + returned.add(itm); + } + + if (count % size != 0) { + /* + * We need to pad + */ + int needed = count % size; + int threshold = 0; + + while (needed > 0 && threshold <= MAX_NTRIESPART) { + final E val = padder.get(); + final int newCount = counter.apply(val); + + if (newCount <= needed) { + returned.add(val); + + threshold = 0; + + needed -= newCount; + } else { + threshold += 1; + } + } + + if (threshold > MAX_NTRIESPART) { + final String fmt = "Heuristic (more than %d iterations of attempting to pad) detected an unpaddable list. (%s)\nPartially padded list: %S"; + + final String msg = String.format(fmt, MAX_NTRIESPART, list.toString(), returned.toString()); + + throw new IllegalArgumentException(msg); + } + } + + return returned; + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/NumberUtils.java b/base/src/main/java/bjc/utils/funcutils/NumberUtils.java new file mode 100644 index 0000000..770d3a5 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/NumberUtils.java @@ -0,0 +1,69 @@ +package bjc.utils.funcutils; + +import java.util.function.Function; + +/** + * Utility functions for dealing with numbers + * + * @author ben + * + */ +public class NumberUtils { + /** + * Compute the falling factorial of a number + * + * @param value + * The number to compute + * @param power + * The power to do the falling factorial for + * @return The falling factorial of the number to the power + */ + public static int fallingFactorial(final int value, final int power) { + if (power == 0) + return 1; + else if (power == 1) + return value; + else { + int result = 1; + + for (int currentSub = 0; currentSub < power + 1; currentSub++) { + result *= value - currentSub; + } + + return result; + } + } + + /** + * Evaluates a linear probability distribution + * + * @param winning + * The number of winning possibilities + * @param total + * The number of total possibilities + * @param rng + * The function to use to generate a random possibility + * @return Whether or not a random possibility was a winning one + */ + public static boolean isProbable(final int winning, final int total, final Function rng) { + return rng.apply(total) < winning; + } + + /** + * Check if a number is in an inclusive range. + * + * @param min + * The minimum value of the range. + * + * @param max + * The maximum value of the range. + * + * @param i + * The number to check. + * + * @return Whether the number is in the range. + */ + public static boolean between(final int min, final int max, final int i) { + return i >= min && i <= max; + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/StringUtils.java b/base/src/main/java/bjc/utils/funcutils/StringUtils.java new file mode 100644 index 0000000..62f78f5 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/StringUtils.java @@ -0,0 +1,196 @@ +package bjc.utils.funcutils; + +import java.util.Deque; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.ibm.icu.text.BreakIterator; + +/** + * Utility methods for operations on strings + * + * @author ben + * + */ +public class StringUtils { + /** + * Check if a string consists only of one or more matches of a regular + * expression + * + * @param input + * The string to check + * @param rRegex + * The regex to see if the string only contains matches + * of + * @return Whether or not the string consists only of multiple matches + * of the provided regex + */ + public static boolean containsOnly(final String input, final String rRegex) { + if (input == null) + throw new NullPointerException("Input must not be null"); + else if (rRegex == null) throw new NullPointerException("Regex must not be null"); + + /* + * This regular expression is fairly simple. + * + * First, we match the beginning of the string. Then, we start a + * non-capturing group whose contents are the passed in regex. + * That group is then matched one or more times and the pattern + * matches to the end of the string + */ + return input.matches("\\A(?:" + rRegex + ")+\\Z"); + } + + /** + * Indent the string being built in a StringBuilder n levels + * + * @param builder + * The builder to indent in + * @param levels + * The number of levels to indent + */ + public static void indentNLevels(final StringBuilder builder, final int levels) { + for (int i = 0; i < levels; i++) { + builder.append("\t"); + } + } + + /** + * Print out a deque with a special case for easily showing a deque is + * empty + * + * @param + * The type in the deque + * @param queue + * The deque to print + * @return A string version of the deque, with allowance for an empty + * deque + */ + public static String printDeque(final Deque queue) { + return queue.isEmpty() ? "(none)" : queue.toString(); + } + + /** + * Converts a sequence to an English list. + * + * @param objects + * The sequence to convert to an English list. + * @param join + * The string to use for separating the last element from + * the rest. + * @param comma + * The string to use as a comma + * + * @return The sequence as an English list. + */ + public static String toEnglishList(final Object[] objects, final String join, final String comma) { + if (objects == null) throw new NullPointerException("Sequence must not be null"); + + final StringBuilder sb = new StringBuilder(); + + final String joiner = join; + final String coma = comma; + + switch (objects.length) { + case 0: + /* + * Empty list. + */ + break; + case 1: + /* + * One item. + */ + sb.append(objects[0].toString()); + break; + case 2: + /* + * Two items. + */ + sb.append(objects[0].toString()); + sb.append(" " + joiner + " "); + sb.append(objects[1].toString()); + break; + default: + /* + * Three or more items. + */ + for (int i = 0; i < objects.length - 1; i++) { + sb.append(objects[i].toString()); + sb.append(coma + " "); + } + /* + * Uncomment this to remove serial commas. + * + * int lc = sb.length() - 1; + * + * sb.delete(lc - coma.length(), lc); + */ + sb.append(joiner + " "); + sb.append(objects[objects.length - 1].toString()); + } + + return sb.toString(); + } + + /** + * Converts a sequence to an English list. + * + * @param objects + * The sequence to convert to an English list. + * @param join + * The string to use for separating the last element from + * the rest. + * + * @return The sequence as an English list. + */ + public static String toEnglishList(final Object[] objects, final String join) { + return toEnglishList(objects, join, ","); + } + + /** + * Converts a sequence to an English list. + * + * @param objects + * The sequence to convert to an English list. + * @param and + * Whether to use 'and' or 'or'. + * + * @return The sequence as an English list. + */ + public static String toEnglishList(final Object[] objects, final boolean and) { + if (and) + return toEnglishList(objects, "and"); + else return toEnglishList(objects, "or"); + } + + /** + * Count the number of graphemes in a string. + * + * @param value + * The string to check. + * + * @return The number of graphemes in the string. + */ + public static int graphemeCount(final String value) { + final BreakIterator it = BreakIterator.getCharacterInstance(); + it.setText(value); + + int count = 0; + while (it.next() != BreakIterator.DONE) { + count++; + } + + return count; + } + + public static int countMatches(final String value, final String pattern) { + Matcher mat = Pattern.compile(pattern).matcher(value); + + int num = 0; + while(mat.find()) + num += 1; + + return num; + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/TreeUtils.java b/base/src/main/java/bjc/utils/funcutils/TreeUtils.java new file mode 100644 index 0000000..dcd5738 --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/TreeUtils.java @@ -0,0 +1,56 @@ +package bjc.utils.funcutils; + +import java.util.LinkedList; +import java.util.function.Predicate; + +import bjc.utils.data.ITree; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * Implements various utilities for trees. + * + * @author Benjamin Culkin + */ +public class TreeUtils { + /* + * Convert a tree into a list of outline nodes that match a certain + * path. + */ + public static IList> outlineTree(ITree tre, Predicate leafMarker) { + IList> paths = new FunctionalList<>(); + + LinkedList path = new LinkedList<>(); + path.add(tre.getHead()); + + tre.doForChildren((child) -> findPath(child, path, leafMarker, paths)); + + return paths; + } + + private static void findPath(ITree subtree, LinkedList path, Predicate leafMarker, IList> paths) { + if(subtree.getChildrenCount() == 0 && leafMarker.test(subtree.getHead())) { + /* + * We're at a matching leaf node. Add it. + */ + IList finalPath = new FunctionalList<>(); + + for(T ePath : path) { + finalPath.add(ePath); + } + + finalPath.add(subtree.getHead()); + + paths.add(finalPath); + } else { + /* + * Check the children of this node. + */ + path.add(subtree.getHead()); + + subtree.doForChildren((child) -> findPath(child, path, leafMarker, paths)); + + path.removeLast(); + } + } +} diff --git a/base/src/main/java/bjc/utils/funcutils/TriConsumer.java b/base/src/main/java/bjc/utils/funcutils/TriConsumer.java new file mode 100644 index 0000000..f30386c --- /dev/null +++ b/base/src/main/java/bjc/utils/funcutils/TriConsumer.java @@ -0,0 +1,31 @@ +package bjc.utils.funcutils; + +/** + * Consumer that takes three arguments. + * + * @author EVE + * + * @param + * Type of the first argument. + * @param + * Type of the second argument. + * @param + * Type of the third argument. + * + */ +@FunctionalInterface +public interface TriConsumer { + /** + * Perform the action. + * + * @param a + * The first parameter. + * + * @param b + * The second parameter. + * + * @param c + * The third parameter. + */ + public void accept(A a, B b, C c); +} diff --git a/base/src/main/java/bjc/utils/gen/RandomGrammar.java b/base/src/main/java/bjc/utils/gen/RandomGrammar.java new file mode 100644 index 0000000..3de08d6 --- /dev/null +++ b/base/src/main/java/bjc/utils/gen/RandomGrammar.java @@ -0,0 +1,69 @@ +package bjc.utils.gen; + +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IList; + +/** + * A weighted grammar where all the rules have a equal chance of occuring. + * + * @author ben + * + * @param + * The type of grammar elements to use. + */ +public class RandomGrammar extends WeightedGrammar { + /** + * Create a new random grammar. + */ + public RandomGrammar() { + rules = new FunctionalMap<>(); + } + + /** + * Add cases to a specified rule. + * + * @param rule + * The name of the rule to add cases to. + * @param cases + * The cases to add for this rule. + */ + @SafeVarargs + public final void addCases(final E rule, final IList... cases) { + for (final IList currentCase : cases) { + super.addCase(rule, 1, currentCase); + } + } + + /** + * Create a rule with the specified name and cases. + * + * @param rule + * The name of the rule to add. + * @param cases + * The cases to add for this rule. + */ + @SafeVarargs + public final void makeRule(final E rule, final IList... cases) { + super.addRule(rule); + + for (final IList currentCase : cases) { + super.addCase(rule, 1, currentCase); + } + } + + /** + * Create a rule with the specified name and cases. + * + * @param rule + * The name of the rule to add. + * @param cases + * The cases to add for this rule. + */ + public void makeRule(final E rule, final IList> 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/base/src/main/java/bjc/utils/gen/WeightedGrammar.java b/base/src/main/java/bjc/utils/gen/WeightedGrammar.java new file mode 100644 index 0000000..7777ad8 --- /dev/null +++ b/base/src/main/java/bjc/utils/gen/WeightedGrammar.java @@ -0,0 +1,573 @@ +package bjc.utils.gen; + +import java.util.Random; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import bjc.utils.data.IPair; +import bjc.utils.data.Pair; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; + +/** + * A random grammar, where certain rules will come up more often than others. + * + * @author ben + * + * @param + * The values that make up sentences of this grammar. + */ +public class WeightedGrammar { + /** + * The initial rule of the grammar + */ + protected String initialRule; + + /** + * The rules currently in this grammar + */ + protected IMap>> rules; + + /** + * The random number generator used for random numbers + */ + private Random rng; + + /** + * All of the subgrammars of this grammar + */ + protected IMap> subgrammars; + + /** + * Rules that require special handling + */ + private IMap>> specialRules; + + /** + * Predicate for marking special tokens + */ + + private Predicate specialMarker; + + /** + * Action for special tokens + */ + private BiFunction, IList> specialAction; + + /** + * Create a new weighted grammar. + */ + public WeightedGrammar() { + rules = new FunctionalMap<>(); + subgrammars = new FunctionalMap<>(); + specialRules = new FunctionalMap<>(); + } + + /** + * Create a new weighted grammar that uses the specified source of + * randomness. + * + * @param source + * The source of randomness to use + */ + public WeightedGrammar(final Random source) { + this(); + + if (source == null) throw new NullPointerException("Source of randomness must be non-null"); + + rng = source; + } + + /** + * Configure the action to perform on special tokens. + * + * @param marker + * The marker to find special tokens. + * + * @param action + * The action to take on those tokens. + */ + public void configureSpecial(final Predicate marker, + final BiFunction, IList> action) { + specialMarker = marker; + specialAction = action; + } + + /** + * Adds a special rule to the grammar. + * + * @param ruleName + * The name of the special rule. + * + * @param cse + * The case for the rule. + */ + public void addSpecialRule(final E ruleName, final Supplier> cse) { + if (ruleName == null) + throw new NullPointerException("Rule name must not be null"); + else if (cse == null) throw new NullPointerException("Case must not be null"); + + specialRules.put(ruleName, cse); + } + + /** + * Add a case to an already existing 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(final E ruleName, final int probability, final IList 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); + } + + /** + * Add a alias for an existing subgrammar + * + * @param name + * The name of the subgrammar to alias + * @param alias + * The alias of the subgrammar + * @return Whether the alias was succesfully created + */ + public boolean addGrammarAlias(final E name, final 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; + + if (subgrammars.containsKey(name)) { + subgrammars.put(alias, subgrammars.get(name)); + return true; + } + + return false; + } + + /** + * Add a new rule with no cases. + * + * @param name + * The name of the rule to add. + * @return Whether or not the rule was successfully added. + */ + public boolean addRule(final E name) { + if (rng == null) { + rng = new Random(); + } + + if (name == null) throw new NullPointerException("Rule name must not be null"); + + return addRule(name, new WeightedRandom<>(rng)); + } + + /** + * Add a new rule with a set of cases. + * + * @param name + * The name of the rule to add. + * @param cases + * The set of cases for the rule. + * @return Whether or not the rule was succesfully added. + */ + public boolean addRule(final E name, final 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; + + rules.put(name, cases); + return true; + } + + /** + * Add a subgrammar. + * + * @param name + * The name of the subgrammar. + * @param subgrammar + * The subgrammar to add. + * @return Whether or not the subgrammar was succesfully added. + */ + public boolean addSubgrammar(final E name, final 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; + + subgrammars.put(name, subgrammar); + return true; + } + + /** + * Remove a rule with the specified name. + * + * @param name + * The name of the rule to remove. + */ + public void deleteRule(final E name) { + if (name == null) throw new NullPointerException("Rule name must not be null"); + + rules.remove(name); + } + + /** + * Remove a subgrammar with the specified name. + * + * @param name + * The name of the subgrammar to remove. + */ + public void deleteSubgrammar(final E name) { + if (name == null) throw new NullPointerException("Rule name must not be null"); + + subgrammars.remove(name); + } + + /** + * Generate a set of debug sentences for the specified rule. + * + * Only generates sentences one layer deep. + * + * @param ruleName + * The rule to test. + * @return A set of sentences generated by the specified rule. + */ + public IList> generateDebugValues(final E ruleName) { + if (ruleName == null) throw new NullPointerException("Rule name must not be null"); + + final IList> returnedList = new FunctionalList<>(); + + final WeightedRandom> ruleGenerator = rules.get(ruleName); + + for (int i = 0; i < 10; i++) { + returnedList.add(ruleGenerator.generateValue()); + } + + return returnedList; + } + + /** + * Generate a generic sentence from a initial rule. + * + * @param + * The type of the transformed output + * + * @param initRules + * The initial rule to start with. + * + * @param tokenTransformer + * The function to transform grammar output into + * something. + * + * @param spacer + * The spacer element to add in between output tokens. + * + * @return A randomly generated sentence from the specified initial + * rule. + */ + public IList generateGenericValues(final E initRules, final Function tokenTransformer, + final T spacer) { + if (initRules == 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"); + + final IList returnedList = new FunctionalList<>(); + + IList genRules = new FunctionalList<>(initRules); + + if (specialMarker != null) { + if (specialMarker.test(initRules)) { + genRules = specialAction.apply(initRules, this); + } + } + + for (final E initRule : genRules.toIterable()) { + if (specialRules.containsKey(initRule)) { + for (final E rulePart : specialRules.get(initRule).get().toIterable()) { + final Iterable generatedRuleParts = generateGenericValues(rulePart, + tokenTransformer, spacer).toIterable(); + + for (final T generatedRulePart : generatedRuleParts) { + returnedList.add(generatedRulePart); + returnedList.add(spacer); + } + } + } else if (subgrammars.containsKey(initRule)) { + final Iterable ruleParts = subgrammars.get(initRule) + .generateGenericValues(initRule, tokenTransformer, spacer).toIterable(); + + for (final T rulePart : ruleParts) { + returnedList.add(rulePart); + returnedList.add(spacer); + } + } else if (rules.containsKey(initRule)) { + final Iterable ruleParts = rules.get(initRule).generateValue().toIterable(); + + for (final E rulePart : ruleParts) { + final Iterable generatedRuleParts = generateGenericValues(rulePart, + tokenTransformer, spacer).toIterable(); + + for (final T generatedRulePart : generatedRuleParts) { + returnedList.add(generatedRulePart); + returnedList.add(spacer); + } + } + } else { + final T transformedToken = tokenTransformer.apply(initRule); + + if (transformedToken == null) + throw new NullPointerException("Transformer created null token"); + + returnedList.add(transformedToken); + returnedList.add(spacer); + } + } + + return returnedList; + } + + /** + * Generate a random list of grammar elements from a given initial rule. + * + * @param initRule + * The initial rule to start with. + * @param spacer + * The item to use to space the list. + * @return A list of random grammar elements generated by the specified + * rule. + */ + public IList generateListValues(final E initRule, final E spacer) { + final IList retList = generateGenericValues(initRule, strang -> strang, spacer); + + return retList; + } + + /** + * Get the initial rule of this grammar + * + * @return The initial rule of this grammar + */ + public String getInitialRule() { + return initialRule; + } + + /** + * Returns the number of rules in this grammar + * + * @return The number of rules in this grammar + */ + public int getRuleCount() { + return rules.size(); + } + + /** + * Returns a set containing all of the rules in this grammar + * + * @return The set of all rule names in this grammar + */ + public IList getRuleNames() { + final IList ruleNames = new FunctionalList<>(); + + ruleNames.addAll(rules.keyList()); + ruleNames.addAll(specialRules.keyList()); + + return ruleNames; + } + + /** + * Get the subgrammar with the specified name. + * + * @param name + * The name of the subgrammar to get. + * @return The subgrammar with the specified name. + */ + public WeightedGrammar getSubgrammar(final E name) { + if (name == null) throw new NullPointerException("Subgrammar name must not be null"); + + return subgrammars.get(name); + } + + /** + * Check if this grammar has an initial rule + * + * @return Whether or not this grammar has an initial rule + */ + public boolean hasInitialRule() { + return initialRule != null && !initialRule.equalsIgnoreCase(""); + } + + /** + * Check if this grammar has a given rule. + * + * @param ruleName + * The rule to check for. + * + * @return Whether or not the grammar has a rule by that name. + */ + public boolean hasRule(final E ruleName) { + return rules.containsKey(ruleName) || specialRules.containsKey(ruleName); + } + + /** + * Prefix a given rule with a token multiple times + * + * @param ruleName + * The name of the rule to prefix + * @param prefixToken + * The token to prefix to the rules + * @param additionalProbability + * The additional probability of the tokens + * @param numberOfTimes + * The number of times to prefix the token + */ + public void multiPrefixRule(final E ruleName, final E prefixToken, final int additionalProbability, + final 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."); + + final WeightedRandom> rule = rules.get(ruleName); + + final IList>> newResults = new FunctionalList<>(); + + rule.getValues().forEach((pair) -> { + final IList> newRule = new FunctionalList<>(); + + for (int i = 1; i <= numberOfTimes; i++) { + final IList newCase = pair.merge((left, right) -> { + final IList returnVal = new FunctionalList<>(); + + for (final E val : right.toIterable()) { + returnVal.add(val); + } + + return returnVal; + }); + + for (int j = 1; j <= i; j++) { + newCase.prepend(prefixToken); + } + + newRule.add(newCase); + } + + newRule.forEach((list) -> { + final Integer currentProb = pair.merge((left, right) -> left); + + newResults.add(new Pair<>(currentProb + additionalProbability, list)); + }); + }); + + newResults.forEach((pair) -> { + pair.doWith((left, right) -> { + addCase(ruleName, left, right); + }); + }); + } + + /** + * Create a series of alternatives for a rule by prefixing them with a + * given token + * + * @param additionalProbability + * The amount to adjust the probability by + * @param ruleName + * The name of the rule to prefix + * @param prefixToken + * The token to prefix to the rule + */ + public void prefixRule(final E ruleName, final E prefixToken, final 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"); + + final WeightedRandom> rule = rules.get(ruleName); + + final IList>> newResults = new FunctionalList<>(); + + rule.getValues().forEach((pair) -> { + final IList newCase = pair.merge((left, right) -> { + final IList returnVal = new FunctionalList<>(); + + for (final E val : right.toIterable()) { + returnVal.add(val); + } + + return returnVal; + }); + + newCase.prepend(prefixToken); + + newResults.add(new Pair<>(pair.merge((left, right) -> left) + additionalProbability, newCase)); + }); + + newResults.forEach((pair) -> pair.doWith((left, right) -> addCase(ruleName, left, right))); + } + + /** + * Set the initial rule of the graphic + * + * @param initRule + * The initial rule of this grammar + */ + public void setInitialRule(final String initRule) { + this.initialRule = initRule; + } + + /** + * Suffix a token to a rule + * + * @param ruleName + * The rule to suffix + * @param suffixToken + * The token to prefix to the rule + * @param additionalProbability + * Additional probability of the prefixed rule + */ + public void suffixRule(final E ruleName, final E suffixToken, final 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"); + + final WeightedRandom> rule = rules.get(ruleName); + + final IList>> newResults = new FunctionalList<>(); + + rule.getValues().forEach((par) -> { + final IList newCase = par.merge((left, right) -> { + final IList returnVal = new FunctionalList<>(); + + for (final E val : right.toIterable()) { + returnVal.add(val); + } + + return returnVal; + }); + + newCase.add(suffixToken); + + newResults.add(new Pair<>(par.merge((left, right) -> left) + additionalProbability, newCase)); + }); + + newResults.forEach((pair) -> pair.doWith((left, right) -> addCase(ruleName, left, right))); + } +} diff --git a/base/src/main/java/bjc/utils/gen/WeightedRandom.java b/base/src/main/java/bjc/utils/gen/WeightedRandom.java new file mode 100644 index 0000000..18225ef --- /dev/null +++ b/base/src/main/java/bjc/utils/gen/WeightedRandom.java @@ -0,0 +1,112 @@ +package bjc.utils.gen; + +import java.util.Random; + +import bjc.utils.data.IHolder; +import bjc.utils.data.IPair; +import bjc.utils.data.Identity; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * Represents a random number generator where certain results are weighted more + * heavily than others. + * + * @author ben + * + * @param + * The type of values that are randomly selected. + */ +public class WeightedRandom { + /* + * The list of probabilities for each result + */ + private final IList probabilities; + + /* + * The list of possible results to pick from + */ + private final IList results; + + /* + * The source for any needed random numbers + */ + private final Random source; + + private int totalChance; + + /** + * Create a new weighted random generator with the specified source of + * randomness + * + * @param src + * The source of randomness to use. + */ + public WeightedRandom(final Random src) { + probabilities = new FunctionalList<>(); + results = new FunctionalList<>(); + + if (src == null) throw new NullPointerException("Source of randomness must not be null"); + + source = src; + } + + /** + * Add a probability for a specific result to be given. + * + * @param chance + * The chance to get this result. + * @param result + * The result to get when the chance comes up. + */ + public void addProbability(final int chance, final E result) { + probabilities.add(chance); + results.add(result); + + totalChance += chance; + } + + /** + * Generate a weighted random value. + * + * @return A random value selected in a weighted fashion. + */ + public E generateValue() { + final IHolder value = new Identity<>(source.nextInt(totalChance)); + final IHolder current = new Identity<>(); + final IHolder picked = new Identity<>(true); + + probabilities.forEachIndexed((index, probability) -> { + if (picked.unwrap(bool -> bool)) { + if (value.unwrap((number) -> number < probability)) { + current.transform((result) -> results.getByIndex(index)); + + picked.transform((bool) -> false); + } else { + value.transform((number) -> number - probability); + } + } + }); + + return current.unwrap((result) -> result); + } + + /** + * Return a list of values that can be generated by this generator + * + * @return A list of all the values that can be generated + */ + public IList getResults() { + return results; + } + + /** + * Return a list containing values that can be generated paired with the + * probability of those values being generated + * + * @return A list of pairs of values and value probabilities + */ + public IList> getValues() { + return probabilities.pairWith(results); + } +} diff --git a/base/src/main/java/bjc/utils/graph/AdjacencyMap.java b/base/src/main/java/bjc/utils/graph/AdjacencyMap.java new file mode 100644 index 0000000..446ab5b --- /dev/null +++ b/base/src/main/java/bjc/utils/graph/AdjacencyMap.java @@ -0,0 +1,216 @@ +package bjc.utils.graph; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.InputMismatchException; +import java.util.Scanner; + +import bjc.utils.data.IHolder; +import bjc.utils.data.Identity; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; +import bjc.utils.funcutils.FuncUtils; + +/** + * An adjacency map representing a graph + * + * @author ben + * + * @param + * The type of the nodes in the graph + */ +public class AdjacencyMap { + /** + * Create an adjacency map from a stream of text + * + * @param stream + * The stream of text to read in + * @return An adjacency map defined by the text + */ + public static AdjacencyMap fromStream(final InputStream stream) { + if (stream == null) throw new NullPointerException("Input source must not be null"); + + // Create the adjacency map + AdjacencyMap adjacency; + + try (Scanner input = new Scanner(stream)) { + input.useDelimiter("\n"); + + int vertexCount; + + final String possible = input.next(); + + try { + // First, read in number of vertices + vertexCount = Integer.parseInt(possible); + } catch (final NumberFormatException nfex) { + final InputMismatchException imex = new InputMismatchException( + "The first line must contain the number of vertices. " + possible + + " is not a valid number"); + + imex.initCause(nfex); + + throw imex; + } + + if (vertexCount <= 0) + throw new InputMismatchException("The number of vertices must be greater than 0"); + + final IList vertices = new FunctionalList<>(); + + FuncUtils.doTimes(vertexCount, (vertexNo) -> vertices.add(vertexNo)); + + adjacency = new AdjacencyMap<>(vertices); + + final IHolder row = new Identity<>(0); + + input.forEachRemaining((strang) -> { + readRow(adjacency, vertexCount, row, strang); + }); + } + + return adjacency; + } + + private static void readRow(final AdjacencyMap adjacency, final int vertexCount, + final IHolder row, final String strang) { + final String[] parts = strang.split(" "); + + if (parts.length != vertexCount) + throw new InputMismatchException("Must specify a weight for all " + vertexCount + " vertices"); + + int column = 0; + + for (final String part : parts) { + int weight; + + try { + weight = Integer.parseInt(part); + } catch (final NumberFormatException nfex) { + final InputMismatchException imex = new InputMismatchException( + "" + part + " is not a valid weight."); + + imex.initCause(nfex); + + throw imex; + } + + adjacency.setWeight(row.getValue(), column, weight); + + column++; + } + + row.transform((rowNumber) -> rowNumber + 1); + } + + /** + * The backing storage of the map + */ + private final IMap> adjacency = new FunctionalMap<>(); + + /** + * Create a new map from a set of vertices + * + * @param vertices + * The set of vertices to create a map from + */ + public AdjacencyMap(final IList vertices) { + if (vertices == null) throw new NullPointerException("Vertices must not be null"); + + vertices.forEach(vertex -> { + final IMap row = new FunctionalMap<>(); + + vertices.forEach(target -> { + row.put(target, 0); + }); + + adjacency.put(vertex, row); + }); + } + + /** + * Check if the graph is directed + * + * @return Whether or not the graph is directed + */ + public boolean isDirected() { + final IHolder result = new Identity<>(true); + + adjacency.forEach((sourceKey, sourceValue) -> { + sourceValue.forEach((targetKey, targetValue) -> { + final int inverseValue = adjacency.get(targetKey).get(sourceKey); + + if (targetValue != inverseValue) { + result.replace(false); + } + }); + }); + + return result.getValue(); + } + + /** + * Set the weight of an edge + * + * @param source + * The source node of the edge + * @param target + * The target node of the edge + * @param weight + * The weight of the edge + */ + public void setWeight(final T source, final T target, final int weight) { + if (source == null) + throw new NullPointerException("Source vertex must not be null"); + else if (target == null) throw new NullPointerException("Target vertex must not be null"); + + if (!adjacency.containsKey(source)) + throw new IllegalArgumentException("Source vertex " + source + " isn't present in map"); + else if (!adjacency.containsKey(target)) + throw new IllegalArgumentException("Target vertex " + target + " isn't present in map"); + + adjacency.get(source).put(target, weight); + } + + /** + * Convert this to a different graph representation + * + * @return The new representation of this graph + */ + public Graph toGraph() { + final Graph ret = new Graph<>(); + + adjacency.forEach((sourceKey, sourceValue) -> { + sourceValue.forEach((targetKey, targetValue) -> { + ret.addEdge(sourceKey, targetKey, targetValue, true); + }); + }); + + return ret; + } + + /** + * Convert an adjacency map back into a stream + * + * @param sink + * The stream to convert to + */ + public void toStream(final OutputStream sink) { + if (sink == null) throw new NullPointerException("Output source must not be null"); + + final PrintStream outputPrinter = new PrintStream(sink); + + adjacency.forEach((sourceKey, sourceValue) -> { + sourceValue.forEach((targetKey, targetValue) -> { + outputPrinter.printf("%d", targetValue); + }); + + outputPrinter.println(); + }); + + outputPrinter.close(); + } +} diff --git a/base/src/main/java/bjc/utils/graph/Edge.java b/base/src/main/java/bjc/utils/graph/Edge.java new file mode 100644 index 0000000..0152e3d --- /dev/null +++ b/base/src/main/java/bjc/utils/graph/Edge.java @@ -0,0 +1,112 @@ +package bjc.utils.graph; + +/** + * An edge in a weighted graph + * + * @author ben + * + * @param + * The type of the nodes in the graph + */ +public class Edge { + /* + * The distance from initial to terminal node + */ + private final int distance; + + /* + * The initial and terminal nodes of this edge + */ + private final T source, target; + + /** + * Create a new edge with set parameters + * + * @param initial + * The initial node of the edge + * @param terminal + * The terminal node of the edge + * @param distance + * The distance between initial and terminal edge + */ + public Edge(final T initial, final T terminal, final int distance) { + if (initial == null) + throw new NullPointerException("Initial node must not be null"); + else if (terminal == null) throw new NullPointerException("Terminal node must not be null"); + + this.source = initial; + this.target = terminal; + this.distance = distance; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) + return true; + else if (obj == null) + return false; + else if (getClass() != obj.getClass()) + return false; + else { + final 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; + } + } + + /** + * Get the distance in this edge + * + * @return The distance between the initial and terminal nodes of this + * edge + */ + public int getDistance() { + return distance; + } + + /** + * Get the initial node of an edge + * + * @return The initial node of this edge + */ + public T getSource() { + return source; + } + + /** + * Get the target node of an edge + * + * @return The target node of this edge + */ + public T getTarget() { + return target; + } + + @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; + } + + @Override + public String toString() { + return " first vertex " + source + " to vertex " + target + " with distance: " + distance; + } +} diff --git a/base/src/main/java/bjc/utils/graph/Graph.java b/base/src/main/java/bjc/utils/graph/Graph.java new file mode 100644 index 0000000..280a7f5 --- /dev/null +++ b/base/src/main/java/bjc/utils/graph/Graph.java @@ -0,0 +1,267 @@ +package bjc.utils.graph; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; + +import bjc.utils.data.IHolder; +import bjc.utils.data.Identity; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; + +/** + * A directed weighted graph, where the vertices have some arbitrary label + * + * @author ben + * + * @param + * The label for vertices + */ +public class Graph { + /** + * Create a graph from a list of edges + * + * @param + * The type of data stored in the edges + * + * @param edges + * The list of edges to build from + * @return A graph built from the provided edge-list + */ + public static Graph fromEdgeList(final List> edges) { + final Graph g = new Graph<>(); + + edges.forEach(edge -> { + g.addEdge(edge.getSource(), edge.getTarget(), edge.getDistance(), true); + }); + + return g; + } + + /** + * The backing representation of the graph + */ + private final IMap> backing; + + /** + * Create a new graph + */ + public Graph() { + backing = new FunctionalMap<>(); + } + + /** + * Add a edge to the graph + * + * @param source + * The source vertex for this edge + * @param target + * The target vertex for this edge + * @param distance + * The distance from the source vertex to the target + * vertex + * @param directed + * Whether or not + */ + public void addEdge(final T source, final T target, final int distance, final boolean directed) { + // Can't add edges with a null source or target + if (source == null) + throw new NullPointerException("The source vertex cannot be null"); + else if (target == null) throw new NullPointerException("The target vertex cannot be null"); + + // Initialize adjacency list for vertices if necessary + if (!backing.containsKey(source)) { + backing.put(source, new FunctionalMap()); + } + + // Add the edge to the graph + backing.get(source).put(target, distance); + + // Handle possible directed edges + if (!directed) { + if (!backing.containsKey(target)) { + backing.put(target, new FunctionalMap()); + } + + backing.get(target).put(source, distance); + } + } + + /** + * Execute an action for all edges of a specific vertex matching + * conditions + * + * @param source + * The vertex to test edges for + * @param matcher + * The conditions an edge must match + * @param action + * The action to execute for matching edges + */ + public void forAllEdgesMatchingAt(final T source, final BiPredicate matcher, + final BiConsumer action) { + if (matcher == null) + throw new NullPointerException("Matcher must not be null"); + else if (action == null) throw new NullPointerException("Action must not be null"); + + getEdges(source).forEach((target, weight) -> { + if (matcher.test(target, weight)) { + action.accept(target, weight); + } + }); + } + + /** + * Get all the edges that begin at a particular source vertex + * + * @param source + * The vertex to use as a source + * @return All of the edges with the specified vertex as a source + */ + public IMap getEdges(final T source) { + // Can't find edges for a null source + if (source == null) + throw new NullPointerException("The source cannot be null."); + else if (!backing.containsKey(source)) + throw new IllegalArgumentException("Vertex " + source + " is not in graph"); + + return backing.get(source); + } + + /** + * Get the initial vertex of the graph + * + * @return The initial vertex of the graph + */ + public T getInitial() { + return backing.keyList().first(); + } + + /** + * Uses Prim's algorothm to calculate a MST for the graph. + * + * If the graph is non-connected, this will lead to unpredictable + * results. + * + * @return a list of edges that constitute the MST + */ + public List> getMinimumSpanningTree() { + // Set of all of the currently available edges + final Queue> available = new PriorityQueue<>(10, + (left, right) -> left.getDistance() - right.getDistance()); + + // The MST of the graph + final List> minimums = new ArrayList<>(); + + // The set of all of the visited vertices. + final Set visited = new HashSet<>(); + + // Start at the initial vertex and visit it + final IHolder source = new Identity<>(getInitial()); + + visited.add(source.getValue()); + + // Make sure we visit all the nodes + while (visited.size() != getVertexCount()) { + // Grab all edges adjacent to the provided edge + + forAllEdgesMatchingAt(source.getValue(), (target, weight) -> { + return !visited.contains(target); + }, (target, weight) -> { + final T vert = source.unwrap(vertex -> vertex); + + available.add(new Edge<>(vert, target, weight)); + }); + + // Get the edge with the minimum distance + final IHolder> minimum = new Identity<>(available.poll()); + + // Only consider edges where we haven't visited the + // target of + // the edge + while (visited.contains(minimum.getValue().getTarget())) { + minimum.transform((edge) -> available.poll()); + } + + // Add it to our MST + minimums.add(minimum.getValue()); + + // Advance to the next node + source.transform((vertex) -> minimum.unwrap(edge -> edge.getTarget())); + + // Visit this node + visited.add(source.getValue()); + } + + return minimums; + } + + /** + * Get the count of the vertices in this graph + * + * @return A count of the vertices in this graph + */ + public int getVertexCount() { + return backing.size(); + } + + /** + * Get all of the vertices in this graph. + * + * @return A unmodifiable set of all the vertices in the graph. + */ + public IList getVertices() { + return backing.keyList(); + } + + /** + * Remove the edge starting at the source and ending at the target + * + * @param source + * The source vertex for the edge + * @param target + * The target vertex for the edge + */ + public void removeEdge(final T source, final T target) { + // Can't remove things w/ null vertices + if (source == null) + throw new NullPointerException("The source vertex cannot be null"); + else if (target == null) throw new NullPointerException("The target vertex cannot be null"); + + // Can't remove if one vertice doesn't exists + if (!backing.containsKey(source)) + throw new NoSuchElementException("vertex " + source + " does not exist."); + + if (!backing.containsKey(target)) + throw new NoSuchElementException("vertex " + target + " does not exist."); + + backing.get(source).remove(target); + + // Uncomment this to turn the graph undirected + // graph.get(target).remove(source); + } + + /** + * Convert a graph into a adjacency map/matrix + * + * @return A adjacency map representing this graph + */ + public AdjacencyMap toAdjacencyMap() { + final AdjacencyMap adjacency = new AdjacencyMap<>(backing.keyList()); + + backing.forEach((sourceKey, sourceValue) -> { + sourceValue.forEach((targetKey, targetValue) -> { + adjacency.setWeight(sourceKey, targetKey, targetValue); + }); + }); + + return adjacency; + } +} diff --git a/base/src/main/java/bjc/utils/gui/ExtensionFileFilter.java b/base/src/main/java/bjc/utils/gui/ExtensionFileFilter.java new file mode 100644 index 0000000..7c487eb --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/ExtensionFileFilter.java @@ -0,0 +1,56 @@ +package bjc.utils.gui; + +import java.io.File; +import java.util.List; + +import javax.swing.filechooser.FileFilter; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * A file filter based on extensions. + * + * Built for Swing. + * + * @author ben + * + */ +public class ExtensionFileFilter extends FileFilter { + /** + * The list holding all filtered extensions + */ + private final IList extensions; + + /** + * Create a new filter only showing files with the specified extensions. + * + * @param exts + * The extensions to show in this filter. + */ + public ExtensionFileFilter(final List exts) { + extensions = new FunctionalList<>(exts); + } + + /** + * Create a new filter only showing files with the specified extensions. + * + * @param exts + * The extensions to show in this filter. + */ + public ExtensionFileFilter(final String... exts) { + extensions = new FunctionalList<>(exts); + } + + @Override + public boolean accept(final File pathname) { + if (pathname == null) throw new NullPointerException("Pathname must not be null"); + + return extensions.anyMatch(pathname.getName()::endsWith); + } + + @Override + public String getDescription() { + return extensions.toString(); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/gui/SimpleDialogs.java b/base/src/main/java/bjc/utils/gui/SimpleDialogs.java new file mode 100644 index 0000000..59eb1c3 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleDialogs.java @@ -0,0 +1,269 @@ +package bjc.utils.gui; + +import java.awt.Component; +import java.awt.Frame; +import java.util.function.Function; +import java.util.function.Predicate; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import bjc.utils.gui.layout.VLayout; + +/** + * Utility class for getting simple input from the user. + * + * @author ben + * + */ +public class SimpleDialogs { + /** + * Get a bounded integer from the user. + * + * @param parent + * The parent component for the dialogs. + * @param title + * The title for the dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @param lowerBound + * The lower integer bound to accept. + * @param upperBound + * The upper integer bound to accept. + * @return A int within the specified bounds. + */ + public static int getBoundedInt(final Component parent, final String title, final String prompt, + final int lowerBound, final int upperBound) { + return getValue(parent, title, prompt, (strang) -> { + try { + final int value = Integer.parseInt(strang); + + return value < upperBound && value > lowerBound; + } catch (final NumberFormatException nfex) { + // We don't care about the specifics of the + // exception, just + // that this value isn't good + return false; + } + }, Integer::parseInt); + } + + /** + * Asks the user to pick an option from a series of choices. + * + * @param + * The type of choices for the user to pick + * + * @param parent + * The parent frame for this dialog + * @param title + * The title of this dialog + * @param question + * The question being asked + * @param choices + * The available choices for the question + * @return The choice the user picked, or null if they didn't pick one + */ + @SuppressWarnings("unchecked") + public static E getChoice(final Frame parent, final String title, final String question, + final 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"); + + final JDialog chooser = new JDialog(parent, title, true); + chooser.setLayout(new VLayout(2)); + + final JPanel questionPane = new JPanel(); + + final JLabel questionText = new JLabel(question); + final JComboBox questionChoices = new JComboBox<>(choices); + + questionPane.add(questionText); + questionPane.add(questionChoices); + + final JPanel buttonPane = new JPanel(); + + final JButton okButton = new JButton("Ok"); + final JButton cancelButton = new JButton("Cancel"); + + okButton.addActionListener((event) -> chooser.dispose()); + cancelButton.addActionListener((event) -> chooser.dispose()); + + buttonPane.add(cancelButton); + buttonPane.add(okButton); + + chooser.add(questionPane); + chooser.add(buttonPane); + + chooser.pack(); + chooser.setVisible(true); + + return (E) questionChoices.getSelectedItem(); + } + + /** + * Get a integer from the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A int. + */ + public static int getInt(final Component parent, final String title, final String prompt) { + return getValue(parent, title, prompt, strang -> { + try { + Integer.parseInt(strang); + return true; + } catch (final NumberFormatException nfex) { + // We don't care about this exception, just mark + // the value + // as not good + return false; + } + }, Integer::parseInt); + } + + /** + * Get a string from the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for the dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A string. + */ + public static String getString(final Component parent, final String title, final 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); + } + + /** + * Get a value parsable from a string from the user. + * + * @param + * The type of the value parsed from the string + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @param validator + * A predicate to determine if a input is valid. + * @param transformer + * The function to transform the string into a value. + * @return The value parsed from a string. + */ + public static E getValue(final Component parent, final String title, final String prompt, + final Predicate validator, final Function transformer) { + if (validator == null) + throw new NullPointerException("Validator must not be null"); + else if (transformer == null) throw new NullPointerException("Transformer must not be null"); + + String input = getString(parent, title, prompt); + + while (!validator.test(input)) { + showError(parent, "I/O Error", "Please enter a valid value"); + + input = getString(parent, title, prompt); + } + + return transformer.apply(input); + } + + /** + * Get a whole number from the user. + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A whole number. + */ + public static int getWhole(final Component parent, final String title, final String prompt) { + return getBoundedInt(parent, title, prompt, 0, Integer.MAX_VALUE); + } + + /** + * Ask the user a Yes/No question. + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param question + * The question to ask the user. + * @return True if the user said yes, false otherwise. + */ + public static boolean getYesNo(final Component parent, final String title, final 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"); + + final int result = JOptionPane.showConfirmDialog(parent, question, title, JOptionPane.YES_NO_OPTION); + + return result == JOptionPane.YES_OPTION ? true : false; + } + + /** + * Show a error message to the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param message + * The error to show the user. + */ + public static void showError(final Component parent, final String title, final String message) { + 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 (message == null) throw new NullPointerException("Error message must not be null"); + + JOptionPane.showMessageDialog(parent, message, title, JOptionPane.ERROR_MESSAGE); + } + + /** + * Show an informative message to the user + * + * @param parent + * The parent for this dialog + * @param title + * Show the title for this dialog + * @param message + * Show the message for this dialog + */ + public static void showMessage(final Component parent, final String title, final String message) { + 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 (message == null) throw new NullPointerException("Message must not be null"); + + JOptionPane.showMessageDialog(parent, title, message, JOptionPane.INFORMATION_MESSAGE); + } +} diff --git a/base/src/main/java/bjc/utils/gui/SimpleFileChooser.java b/base/src/main/java/bjc/utils/gui/SimpleFileChooser.java new file mode 100644 index 0000000..7da0bd8 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleFileChooser.java @@ -0,0 +1,198 @@ +package bjc.utils.gui; + +import java.awt.Component; +import java.io.File; + +import javax.swing.JFileChooser; + +import bjc.utils.exceptions.FileNotChosenException; + +/** + * Utility class for easily prompting user for files. + * + * Built for Swing. + * + * @author ben + * + */ +public class SimpleFileChooser { + private static File doOpenFile(final Component parent, final String title, final JFileChooser files) { + if (title == null) throw new NullPointerException("Title must not be null"); + + files.setDialogTitle(title); + + boolean success = false; + + while (!success) { + try { + maybeDoOpenFile(parent, files); + + success = true; + } catch (final FileNotChosenException fncx) { + // We don't care about specifics + SimpleDialogs.showError(parent, "I/O Error", "Please pick a file to open"); + } + } + + return files.getSelectedFile(); + } + + private static File doSaveFile(final Component parent, final String title, final JFileChooser files) { + if (title == null) throw new NullPointerException("Title must not be null"); + + files.setDialogTitle(title); + + final boolean success = false; + + while (!success) { + try { + maybeDoSaveFile(parent, files); + + return files.getSelectedFile(); + } catch (final FileNotChosenException fncex) { + // We don't care about specifics + SimpleDialogs.showError(parent, "I/O Error", "Please pick a file to save to"); + } + } + } + + /** + * Prompt the user with a "Open File..." dialog. Keeps prompting them + * until they pick a file. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @return The file the user has chosen. + */ + public static File getOpenFile(final Component parent, final String title) { + final JFileChooser files = new JFileChooser(); + + return doOpenFile(parent, title, files); + } + + /** + * Prompt the user with a "Open File..." dialog. Keeps prompting them + * until they pick a file. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @param extensions + * The list of file extensions the file should have. + * @return The file the user has chosen. + */ + public static File getOpenFile(final Component parent, final String title, final String... extensions) { + final JFileChooser files = new JFileChooser(); + + files.addChoosableFileFilter(new ExtensionFileFilter(extensions)); + + return doOpenFile(parent, title, files); + } + + /** + * Prompt the user with a "Save File..." dialog. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @return The file the user chose. + */ + public static File getSaveFile(final Component parent, final String title) { + final JFileChooser files = new JFileChooser(); + + return doSaveFile(parent, title, files); + } + + /** + * Prompt the user with a "Save File..." dialog. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @param extensions + * The extensions of the files the user can choose. + * @return The file the user chose. + */ + public static File getSaveFile(final Component parent, final String title, final String... extensions) { + final JFileChooser files = new JFileChooser(); + + files.addChoosableFileFilter(new ExtensionFileFilter(extensions)); + + return doSaveFile(parent, title, files); + } + + private static void maybeDoOpenFile(final Component parent, final 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"); + + final int result = files.showSaveDialog(parent); + + if (result != JFileChooser.APPROVE_OPTION) throw new FileNotChosenException(); + } + + private static void maybeDoSaveFile(final Component parent, final 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"); + + final int result = files.showSaveDialog(parent); + + if (result != JFileChooser.APPROVE_OPTION) throw new FileNotChosenException(); + } + + /** + * Prompt the user with a "Open File..." dialog. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @return The file if the user chose one or null if they didn't. + */ + public static File maybeOpenFile(final Component parent, final String title) { + if (title == null) throw new NullPointerException("Title must not be null"); + + final JFileChooser files = new JFileChooser(); + files.setDialogTitle(title); + + try { + maybeDoOpenFile(parent, files); + } catch (final FileNotChosenException fncex) { + // We don't care about specifics + } + + return files.getSelectedFile(); + } + + /** + * Prompt the user with a "Save File..." dialog. + * + * @param parent + * The component to use as the parent for the dialog. + * @param title + * The title of the dialog to prompt with. + * @return The file if the user chose one or null if they didn't. + */ + public static File maybeSaveFile(final Component parent, final String title) { + if (title == null) throw new NullPointerException("Title must not be null"); + + final JFileChooser files = new JFileChooser(); + files.setDialogTitle(title); + + try { + maybeDoSaveFile(parent, files); + } catch (final FileNotChosenException fncex) { + // We don't care about specifics + } + + return files.getSelectedFile(); + } +} diff --git a/base/src/main/java/bjc/utils/gui/SimpleInternalDialogs.java b/base/src/main/java/bjc/utils/gui/SimpleInternalDialogs.java new file mode 100644 index 0000000..5237557 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleInternalDialogs.java @@ -0,0 +1,208 @@ +package bjc.utils.gui; + +import java.awt.Component; +import java.util.function.Function; +import java.util.function.Predicate; + +import javax.swing.JOptionPane; + +/** + * Utility class for getting simple input from the user. + * + * Modified to work with JDesktopPanes + * + * @author ben + * + */ +public class SimpleInternalDialogs { + /** + * Get a bounded integer from the user. + * + * @param parent + * The parent component for the dialogs. + * @param title + * The title for the dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @param lowerBound + * The lower integer bound to accept. + * @param upperBound + * The upper integer bound to accept. + * @return A int within the specified bounds. + */ + public static int getBoundedInt(final Component parent, final String title, final String prompt, + final int lowerBound, final int upperBound) { + return getValue(parent, title, prompt, (strang) -> { + try { + final int value = Integer.parseInt(strang); + + return value < upperBound && value > lowerBound; + } catch (final NumberFormatException nfex) { + // We don't care about the specifics of the + // exception, just + // that this value isn't good + return false; + } + }, Integer::parseInt); + } + + /** + * Get a integer from the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A int. + */ + public static int getInt(final Component parent, final String title, final String prompt) { + return getValue(parent, title, prompt, strang -> { + try { + Integer.parseInt(strang); + return true; + } catch (final NumberFormatException nfex) { + // We don't care about this exception, just mark + // the value + // as not good + return false; + } + }, Integer::parseInt); + } + + /** + * Get a string from the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for the dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A string. + */ + public static String getString(final Component parent, final String title, final 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.showInternalInputDialog(parent, prompt, title, JOptionPane.QUESTION_MESSAGE); + } + + /** + * Get a value parsable from a string from the user. + * + * @param + * The type of the value parsed from the string + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @param validator + * A predicate to determine if a input is valid. + * @param transformer + * The function to transform the string into a value. + * @return The value parsed from a string. + */ + public static E getValue(final Component parent, final String title, final String prompt, + final Predicate validator, final Function transformer) { + if (validator == null) + throw new NullPointerException("Validator must not be null"); + else if (transformer == null) throw new NullPointerException("Transformer must not be null"); + + String strang = getString(parent, title, prompt); + + while (!validator.test(strang)) { + showError(parent, "I/O Error", "Please enter a valid value"); + + strang = getString(parent, title, prompt); + } + + return transformer.apply(strang); + } + + /** + * Get a whole number from the user. + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param prompt + * The prompt to tell the user what to enter. + * @return A whole number. + */ + public static int getWhole(final Component parent, final String title, final String prompt) { + return getBoundedInt(parent, title, prompt, 0, Integer.MAX_VALUE); + } + + /** + * Ask the user a Yes/No question. + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param question + * The question to ask the user. + * @return True if the user said yes, false otherwise. + */ + public static boolean getYesNo(final Component parent, final String title, final 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"); + + final int result = JOptionPane.showInternalConfirmDialog(parent, question, title, + JOptionPane.YES_NO_OPTION); + + return result == JOptionPane.YES_OPTION ? true : false; + } + + /** + * Show a error message to the user + * + * @param parent + * The parent component for dialogs. + * @param title + * The title for dialogs. + * @param message + * The error to show the user. + */ + public static void showError(final Component parent, final String title, final String message) { + 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 (message == null) throw new NullPointerException("Error message must not be null"); + + JOptionPane.showInternalMessageDialog(parent, message, title, JOptionPane.ERROR_MESSAGE); + } + + /** + * Show an informative message to the user + * + * @param parent + * The parent for this dialog + * @param title + * Show the title for this dialog + * @param message + * Show the message for this dialog + */ + public static void showMessage(final Component parent, final String title, final String message) { + 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 (message == null) throw new NullPointerException("Message must not be null"); + + JOptionPane.showInternalMessageDialog(parent, title, message, JOptionPane.INFORMATION_MESSAGE); + } +} diff --git a/base/src/main/java/bjc/utils/gui/SimpleInternalFrame.java b/base/src/main/java/bjc/utils/gui/SimpleInternalFrame.java new file mode 100644 index 0000000..afb498e --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleInternalFrame.java @@ -0,0 +1,40 @@ +package bjc.utils.gui; + +import javax.swing.JInternalFrame; + +/** + * A simple internal frame class + * + * @author ben + * + */ +public class SimpleInternalFrame extends JInternalFrame { + private static final long serialVersionUID = -2966801321260716617L; + + /** + * Create a new blank internal frame + */ + public SimpleInternalFrame() { + super(); + } + + /** + * Create a new blank internal frame with a specific title + * + * @param title + * The title of the internal frame + */ + public SimpleInternalFrame(final String title) { + super(title); + } + + protected void setupFrame() { + setSize(320, 240); + + setResizable(true); + + setClosable(true); + setMaximizable(true); + setIconifiable(true); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/gui/SimpleJList.java b/base/src/main/java/bjc/utils/gui/SimpleJList.java new file mode 100644 index 0000000..411d0db --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleJList.java @@ -0,0 +1,49 @@ +package bjc.utils.gui; + +import javax.swing.DefaultListModel; +import javax.swing.JList; +import javax.swing.ListModel; + +/** + * Utility class for making JLists and their models. + * + * @author ben + * + */ +public class SimpleJList { + /** + * Create a new JList from a given list. + * + * @param + * The type of data in the JList + * + * @param source + * The list to populate the JList with. + * @return A JList populated with the elements from ls. + */ + public static JList buildFromList(final Iterable source) { + if (source == null) throw new NullPointerException("Source must not be null"); + + return new JList<>(buildModel(source)); + } + + /** + * Create a new list model from a given list. + * + * @param + * The type of data in the list model + * + * @param source + * The list to fill the list model from. + * @return A list model populated with the elements from ls. + */ + public static ListModel buildModel(final Iterable source) { + if (source == null) throw new NullPointerException("Source must not be null"); + + final DefaultListModel defaultModel = new DefaultListModel<>(); + + source.forEach(defaultModel::addElement); + + return defaultModel; + } +} diff --git a/base/src/main/java/bjc/utils/gui/SimpleTitledBorder.java b/base/src/main/java/bjc/utils/gui/SimpleTitledBorder.java new file mode 100644 index 0000000..9b01507 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/SimpleTitledBorder.java @@ -0,0 +1,25 @@ +package bjc.utils.gui; + +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; + +/** + * A simple border with a title attached to it. + * + * @author ben + * + */ +public class SimpleTitledBorder extends TitledBorder { + // Version ID for serialization + private static final long serialVersionUID = -5655969079949148487L; + + /** + * Create a new border with the specified title. + * + * @param title + * The title for the border. + */ + public SimpleTitledBorder(final String title) { + super(new EtchedBorder(), title); + } +} diff --git a/base/src/main/java/bjc/utils/gui/TextAreaOutputStream.java b/base/src/main/java/bjc/utils/gui/TextAreaOutputStream.java new file mode 100644 index 0000000..fbc58ed --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/TextAreaOutputStream.java @@ -0,0 +1,35 @@ +package bjc.utils.gui; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.swing.JTextArea; + +/** + * An output stream that prints to a JTextArea + * + * @author epr + * @author Levente S\u00e1ntha (lsantha@users.sourceforge.net) + */ +public class TextAreaOutputStream extends OutputStream { + private final JTextArea textArea; + + /** + * Create a new output stream attached to a textarea + * + * @param console + * The textarea to write to + */ + public TextAreaOutputStream(final JTextArea console) { + this.textArea = console; + } + + @Override + public void write(final int b) throws IOException { + textArea.append("" + (char) b); + + if (b == '\n') { + textArea.repaint(); + } + } +} diff --git a/base/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java b/base/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java new file mode 100644 index 0000000..eb60ae2 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java @@ -0,0 +1,50 @@ +package bjc.utils.gui.awt; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.List; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * Filter a set of filenames by extension. + * + * Built for AWT + * + * @author ben + * + */ +public class ExtensionFileFilter implements FilenameFilter { + /** + * The list of extensions to filter + */ + private final IList extensions; + + /** + * Create a new filter only showing files with the specified extensions. + * + * @param exts + * The extensions to show in this filter. + */ + public ExtensionFileFilter(final List exts) { + if (exts == null) throw new NullPointerException("Extensions must not be null"); + + extensions = new FunctionalList<>(exts); + } + + /** + * Create a new filter only showing files with the specified extensions. + * + * @param exts + * The extensions to show in this filter. + */ + public ExtensionFileFilter(final String... exts) { + extensions = new FunctionalList<>(exts); + } + + @Override + public boolean accept(final File directory, final String name) { + return extensions.anyMatch(name::endsWith); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java b/base/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java new file mode 100644 index 0000000..77a4a59 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java @@ -0,0 +1,144 @@ +package bjc.utils.gui.awt; + +import java.awt.FileDialog; +import java.awt.Frame; +import java.io.File; +import java.io.FilenameFilter; + +import bjc.utils.gui.SimpleDialogs; + +/** + * A simple way to get the user to pick a file + * + * Built for AWT. + * + * @author ben + * + */ +public class SimpleFileDialog { + /** + * Prompt the user to pick a file to open + * + * @param parent + * The parent of the file picker + * @param title + * The title of the file picker + * @return The file the user picked + */ + public static File getOpenFile(final Frame parent, final String title) { + return getOpenFile(parent, title, (String[]) null); + } + + /** + * Prompt the user to pick a file to open + * + * @param parent + * The parent of the file picker + * @param title + * The title of the file picker + * @param extensions + * The extensions to accept as valid + * @return The file the user picked + */ + public static File getOpenFile(final Frame parent, final String title, final 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"); + + final FileDialog chooser = new FileDialog(parent, title, FileDialog.LOAD); + + if (extensions != null) { + final FilenameFilter filter = new ExtensionFileFilter(extensions); + chooser.setFilenameFilter(filter); + } + + chooser.setVisible(true); + + while (chooser.getFile() == null) { + SimpleDialogs.showError(parent, "File I/O Error", "Please choose a file to open."); + chooser.setVisible(true); + } + + return chooser.getFiles()[0]; + } + + /** + * Prompt the user to pick a file to open + * + * @param parent + * The parent of the file picker + * @param title + * The title of the file picker + * @param extensions + * The extensions to accept as valid + * @return The file the user picked + */ + public static File[] getOpenFiles(final Frame parent, final String title, final 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"); + + final FileDialog chooser = new FileDialog(parent, title, FileDialog.LOAD); + + if (extensions != null) { + final FilenameFilter filter = new ExtensionFileFilter(extensions); + chooser.setFilenameFilter(filter); + } + + chooser.setMultipleMode(true); + chooser.setVisible(true); + + while (chooser.getFile() == null) { + SimpleDialogs.showError(parent, "File I/O Error", "Please choose a file to open."); + chooser.setVisible(true); + } + + return chooser.getFiles(); + } + + /** + * Prompt the user to pick a file to save + * + * @param parent + * The parent of the file picker + * @param title + * The title of the file picker + * @return The file the user picked + */ + public static File getSaveFile(final Frame parent, final String title) { + return getSaveFile(parent, title, (String[]) null); + } + + /** + * Prompt the user to pick a file to save + * + * @param parent + * The parent of the file picker + * @param title + * The title of the file picker + * @param extensions + * The extensions to accept as valid + * @return The file the user picked + */ + public static File getSaveFile(final Frame parent, final String title, final 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"); + + final FileDialog chooser = new FileDialog(parent, title, FileDialog.SAVE); + + if (extensions != null) { + final FilenameFilter filter = new ExtensionFileFilter(extensions); + chooser.setFilenameFilter(filter); + } + + chooser.setVisible(true); + + while (chooser.getFile() == null) { + SimpleDialogs.showError(parent, "File I/O Error", "Please choose a file to save to."); + chooser.setVisible(true); + } + + return chooser.getFiles()[0]; + } +} diff --git a/base/src/main/java/bjc/utils/gui/layout/AutosizeLayout.java b/base/src/main/java/bjc/utils/gui/layout/AutosizeLayout.java new file mode 100644 index 0000000..6f384f2 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/layout/AutosizeLayout.java @@ -0,0 +1,22 @@ +package bjc.utils.gui.layout; + +import java.awt.GridLayout; + +/** + * A layout that simply holds one component that it auto-resizes whenever it is + * resized. + * + * @author ben + * + */ +public class AutosizeLayout extends GridLayout { + // Version id for serialization + private static final long serialVersionUID = -2495693595953396924L; + + /** + * Create a new auto-size layout. + */ + public AutosizeLayout() { + super(1, 1); + } +} diff --git a/base/src/main/java/bjc/utils/gui/layout/HLayout.java b/base/src/main/java/bjc/utils/gui/layout/HLayout.java new file mode 100644 index 0000000..4ed1661 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/layout/HLayout.java @@ -0,0 +1,25 @@ +package bjc.utils.gui.layout; + +import java.awt.GridLayout; + +/** + * A layout manager that lays out its components horizontally, evenly sizing + * them. + * + * @author ben + * + */ +public class HLayout extends GridLayout { + // Version ID for serialization + private static final long serialVersionUID = 1244964456966270026L; + + /** + * Create a new horizontal layout with the specified number of columns. + * + * @param columns + * The number of columns in this layout. + */ + public HLayout(final int columns) { + super(1, columns); + } +} diff --git a/base/src/main/java/bjc/utils/gui/layout/VLayout.java b/base/src/main/java/bjc/utils/gui/layout/VLayout.java new file mode 100644 index 0000000..6993365 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/layout/VLayout.java @@ -0,0 +1,25 @@ +package bjc.utils.gui.layout; + +import java.awt.GridLayout; + +/** + * A layout that lays out its components vertically, evenly sharing space among + * them. + * + * @author ben + * + */ +public class VLayout extends GridLayout { + // Version ID for serializations + private static final long serialVersionUID = -6417962941602322663L; + + /** + * Create a new vertical layout with the specified number of rows. + * + * @param rows + * The number of rows. + */ + public VLayout(final int rows) { + super(rows, 1); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/DropdownListPanel.java b/base/src/main/java/bjc/utils/gui/panels/DropdownListPanel.java new file mode 100644 index 0000000..4f71d38 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/DropdownListPanel.java @@ -0,0 +1,73 @@ +package bjc.utils.gui.panels; + +import java.awt.BorderLayout; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; + +import bjc.utils.funcdata.IList; +import bjc.utils.gui.layout.AutosizeLayout; +import bjc.utils.gui.layout.HLayout; + +/** + * A panel that allows you to select choices from a dropdown list + * + * @author ben + * + */ +public class DropdownListPanel extends JPanel { + private static final long serialVersionUID = 2719963952350133541L; + + /** + * Create a new dropdown list panel + * + * @param + * The type of items in the dropdown list + * @param type + * The label of the type of items in the list + * @param model + * The model to put items into + * @param choices + * The items to choose from + */ + public DropdownListPanel(final String type, final DefaultListModel model, final IList choices) { + setLayout(new AutosizeLayout()); + + final JPanel itemInputPanel = new JPanel(); + itemInputPanel.setLayout(new BorderLayout()); + + final JPanel addItemPanel = new JPanel(); + addItemPanel.setLayout(new HLayout(2)); + + final JComboBox addItemBox = new JComboBox<>(); + choices.forEach(addItemBox::addItem); + + final JButton addItemButton = new JButton("Add " + type); + + addItemPanel.add(addItemBox); + addItemPanel.add(addItemButton); + + final JList itemList = new JList<>(model); + itemList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + final JButton removeItemButton = new JButton("Remove " + type); + + addItemButton.addActionListener((ev) -> { + model.addElement(addItemBox.getItemAt(addItemBox.getSelectedIndex())); + }); + + removeItemButton.addActionListener((ev) -> { + model.remove(itemList.getSelectedIndex()); + }); + + itemInputPanel.add(addItemPanel, BorderLayout.PAGE_START); + itemInputPanel.add(itemList, BorderLayout.CENTER); + itemInputPanel.add(removeItemButton, BorderLayout.PAGE_END); + + add(itemInputPanel); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/FormattedInputPanel.java b/base/src/main/java/bjc/utils/gui/panels/FormattedInputPanel.java new file mode 100644 index 0000000..2cecf0c --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/FormattedInputPanel.java @@ -0,0 +1,66 @@ +package bjc.utils.gui.panels; + +import java.util.function.Consumer; + +import javax.swing.JFormattedTextField; +import javax.swing.JFormattedTextField.AbstractFormatter; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import bjc.utils.gui.layout.HLayout; + +/** + * A simple panel allowing for input of a single formatted value + * + * @author ben + * + * @param + * The type of value being formatted + */ +public class FormattedInputPanel extends JPanel { + private static final long serialVersionUID = 5232016563558588031L; + + private final JFormattedTextField field; + + /** + * Create a new formatted input panel + * + * @param label + * The label for this panel + * @param length + * The length of this panel + * @param formatter + * The formatter to use for input + * @param reciever + * The action to call whenever the value changes + */ + @SuppressWarnings("unchecked") + public FormattedInputPanel(final String label, final int length, final AbstractFormatter formatter, + final Consumer reciever) { + setLayout(new HLayout(2)); + + final JLabel lab = new JLabel(label); + field = new JFormattedTextField(formatter); + + field.setColumns(length); + field.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT); + field.addPropertyChangeListener("value", (event) -> { + // This is safe, because InputVal should be the type of + // whatever object the formatter is returning + reciever.accept((InputVal) field.getValue()); + }); + + add(lab); + add(field); + } + + /** + * Reset the value in this panel to a specified value + * + * @param value + * The value to set the panel to + */ + public void resetValues(final InputVal value) { + field.setValue(value); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/HolderOutputPanel.java b/base/src/main/java/bjc/utils/gui/panels/HolderOutputPanel.java new file mode 100644 index 0000000..653dace --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/HolderOutputPanel.java @@ -0,0 +1,79 @@ +package bjc.utils.gui.panels; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.Timer; + +import bjc.utils.data.IHolder; +import bjc.utils.gui.layout.HLayout; + +/** + * A panel that outputs a value bound to a {@link IHolder} + * + * @author ben + * + */ +public class HolderOutputPanel extends JPanel { + private static final long serialVersionUID = 166573313903782080L; + + private Timer updater; + private final JLabel value; + private final int nDelay; + private final IHolder val; + + /** + * Create a new display panel, backed by a holder + * + * @param lab + * The label to attach to this field + * @param valueHolder + * The holder to get the value from + * @param nDelay + * The delay in ms between value updates + */ + public HolderOutputPanel(final String lab, final IHolder valueHolder, final int nDelay) { + this.val = valueHolder; + this.nDelay = nDelay; + + setLayout(new HLayout(2)); + + final JLabel label = new JLabel(lab); + value = new JLabel("(stopped)"); + + updater = new Timer(nDelay, (event) -> { + value.setText(valueHolder.getValue()); + }); + + add(label); + add(value); + } + + /** + * Set this panel back to its initial state + */ + public void reset() { + stopUpdating(); + + value.setText("(stopped)"); + + updater = new Timer(nDelay, (event) -> { + value.setText(val.getValue()); + }); + } + + /** + * Start updating the contents of the field from the holder + */ + public void startUpdating() { + updater.start(); + } + + /** + * Stop updating the contents of the field from the holder + */ + public void stopUpdating() { + updater.stop(); + + value.setText(value.getText() + " (stopped)"); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/ListParameterPanel.java b/base/src/main/java/bjc/utils/gui/panels/ListParameterPanel.java new file mode 100644 index 0000000..cca73d5 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/ListParameterPanel.java @@ -0,0 +1,133 @@ +package bjc.utils.gui.panels; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; + +import bjc.utils.funcdata.IList; +import bjc.utils.gui.SimpleJList; +import bjc.utils.gui.layout.HLayout; +import bjc.utils.gui.layout.VLayout; + +/** + * A panel that has a list of objects and ways of manipulating that list + * + * @author ben + * + * @param + * The type of data stored in the list + */ +public class ListParameterPanel extends JPanel { + // Version id for serialization + private static final long serialVersionUID = 3442971104975491571L; + + /** + * Create a new panel using the specified actions for doing things + * + * @param add + * The action that provides items + * @param edit + * The action that edits items + * @param remove + * The action that removes items + */ + public ListParameterPanel(final Supplier add, final Consumer edit, final Consumer remove) { + this(add, edit, remove, null); + } + + /** + * Create a new panel using the specified actions for doing things + * + * @param add + * The action that provides items + * @param edit + * The action that edits items + * @param remove + * The action that removes items + * @param defaults + * The default values to put in the list + */ + public ListParameterPanel(final Supplier add, final Consumer edit, final Consumer remove, + final IList defaults) { + setLayout(new VLayout(2)); + + JList list; + + if (defaults != null) { + list = SimpleJList.buildFromList(defaults.toIterable()); + } else { + list = new JList<>(new DefaultListModel<>()); + } + + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + final JPanel buttonPanel = new JPanel(); + + int numButtons = 0; + + if (add != null) { + numButtons++; + } + + if (edit != null) { + numButtons++; + } + + if (remove != null) { + numButtons++; + } + + buttonPanel.setLayout(new HLayout(numButtons)); + + JButton addParam = null; + + if (add != null) { + addParam = new JButton("Add..."); + addParam.addActionListener((event) -> { + final DefaultListModel model = (DefaultListModel) list.getModel(); + + model.addElement(add.get()); + }); + } + + JButton editParam = null; + + if (edit != null) { + editParam = new JButton("Edit..."); + editParam.addActionListener((event) -> { + edit.accept(list.getSelectedValue()); + }); + } + + JButton removeParam = null; + + if (remove != null) { + removeParam = new JButton("Remove..."); + removeParam.addActionListener((event) -> { + final DefaultListModel model = (DefaultListModel) list.getModel(); + + remove.accept(model.remove(list.getSelectedIndex())); + }); + } + + if (add != null) { + buttonPanel.add(addParam); + } + + if (edit != null) { + buttonPanel.add(editParam); + } + + if (remove != null) { + buttonPanel.add(removeParam); + } + + add(list); + add(buttonPanel); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/SimpleInputPanel.java b/base/src/main/java/bjc/utils/gui/panels/SimpleInputPanel.java new file mode 100644 index 0000000..65c533d --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/SimpleInputPanel.java @@ -0,0 +1,45 @@ +package bjc.utils.gui.panels; + +import java.awt.BorderLayout; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +/** + * A simple component for text input + * + * @author ben + * + */ +public class SimpleInputPanel extends JPanel { + private static final long serialVersionUID = -4734279623645236868L; + + /** + * The text field containing the input value + */ + public final JTextField inputValue; + + /** + * Create a new input panel + * + * @param label + * The label for the field + * @param columns + * The number of columns of text input to take + */ + public SimpleInputPanel(final String label, final int columns) { + setLayout(new BorderLayout()); + + final JLabel inputLabel = new JLabel(label); + + if (columns < 1) { + inputValue = new JTextField(); + } else { + inputValue = new JTextField(columns); + } + + add(inputLabel, BorderLayout.LINE_START); + add(inputValue, BorderLayout.CENTER); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/SimpleListPanel.java b/base/src/main/java/bjc/utils/gui/panels/SimpleListPanel.java new file mode 100644 index 0000000..edc1797 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/SimpleListPanel.java @@ -0,0 +1,93 @@ +package bjc.utils.gui.panels; + +import java.awt.BorderLayout; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; + +import bjc.utils.gui.layout.AutosizeLayout; +import bjc.utils.gui.layout.HLayout; + +/** + * A simple list of strings + * + * @author ben + * + */ +public class SimpleListPanel extends JPanel { + private static final long serialVersionUID = 2719963952350133541L; + + private static void addItem(final DefaultListModel model, final Predicate verifier, + final Consumer onFailure, final JTextField addItemField) { + final String potentialItem = addItemField.getText(); + + if (verifier == null || verifier.test(potentialItem)) { + model.addElement(potentialItem); + } else { + onFailure.accept(potentialItem); + } + + addItemField.setText(""); + } + + /** + * Create a new list panel + * + * @param type + * The type of things in the list + * @param model + * The model to put items into + * @param verifier + * The predicate to use to verify items + * @param onFailure + * The function to call when an item doesn't verify + */ + public SimpleListPanel(final String type, final DefaultListModel model, + final Predicate verifier, final Consumer onFailure) { + setLayout(new AutosizeLayout()); + + final JPanel itemInputPanel = new JPanel(); + itemInputPanel.setLayout(new BorderLayout()); + + final JPanel addItemPanel = new JPanel(); + addItemPanel.setLayout(new HLayout(2)); + + final JTextField addItemField = new JTextField(255); + final JButton addItemButton = new JButton("Add " + type); + + addItemPanel.add(addItemField); + addItemPanel.add(addItemButton); + + final JList itemList = new JList<>(model); + itemList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + final JScrollPane listScroller = new JScrollPane(itemList); + + final JButton removeItemButton = new JButton("Remove " + type); + + addItemButton.addActionListener((ev) -> { + addItem(model, verifier, onFailure, addItemField); + }); + + addItemField.addActionListener((ev) -> { + addItem(model, verifier, onFailure, addItemField); + }); + + removeItemButton.addActionListener((ev) -> { + model.remove(itemList.getSelectedIndex()); + }); + + itemInputPanel.add(addItemPanel, BorderLayout.PAGE_START); + itemInputPanel.add(listScroller, BorderLayout.CENTER); + itemInputPanel.add(removeItemButton, BorderLayout.PAGE_END); + + add(itemInputPanel); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/SimpleSpinnerPanel.java b/base/src/main/java/bjc/utils/gui/panels/SimpleSpinnerPanel.java new file mode 100644 index 0000000..6106182 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/SimpleSpinnerPanel.java @@ -0,0 +1,42 @@ +package bjc.utils.gui.panels; + +import java.awt.BorderLayout; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.SpinnerModel; + +/** + * A simple spinner control + * + * @author ben + * + */ +public class SimpleSpinnerPanel extends JPanel { + private static final long serialVersionUID = -4734279623645236868L; + + /** + * The spinner being used + */ + public final JSpinner inputValue; + + /** + * Create a new spinner panel + * + * @param label + * The label for the spinner + * @param model + * The model to attach to the spinner + */ + public SimpleSpinnerPanel(final String label, final SpinnerModel model) { + setLayout(new BorderLayout()); + + final JLabel inputLabel = new JLabel(label); + + inputValue = new JSpinner(model); + + add(inputLabel, BorderLayout.LINE_START); + add(inputValue, BorderLayout.CENTER); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/SliderInputPanel.java b/base/src/main/java/bjc/utils/gui/panels/SliderInputPanel.java new file mode 100644 index 0000000..e6a6da4 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/SliderInputPanel.java @@ -0,0 +1,187 @@ +package bjc.utils.gui.panels; + +import java.text.ParseException; +import java.util.function.Consumer; + +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; + +import bjc.utils.gui.layout.HLayout; + +/** + * A simple input panel for a slider-controlled value and a manual-input field + * for setting the slider + * + * @author ben + * + */ +public class SliderInputPanel extends JPanel { + private final class NumberFormatter extends JFormattedTextField.AbstractFormatter { + private static final long serialVersionUID = -4448291795913908270L; + + private final int minValue; + private final int maxValue; + + private final int initValue; + + public NumberFormatter(final SliderSettings settings) { + minValue = settings.minValue; + maxValue = settings.maxValue; + + initValue = settings.initValue; + } + + @Override + public Object stringToValue(final String text) throws ParseException { + try { + final int val = Integer.parseInt(text); + + if (val < minValue) + throw new ParseException("Value must be greater than " + minValue, 0); + else if (val > maxValue) + throw new ParseException("Value must be smaller than " + maxValue, 0); + else return val; + } catch (final NumberFormatException nfex) { + final ParseException pex = new ParseException("Value must be a valid integer", 0); + + pex.initCause(nfex); + + throw pex; + } + } + + @Override + public String valueToString(final Object value) throws ParseException { + if (value == null) return Integer.toString(initValue); + + return Integer.toString((Integer) value); + } + } + + /** + * Represents the settings for a slider + * + * @author ben + * + */ + public static class SliderSettings { + /** + * The minimum value of the slider + */ + public final int minValue; + /** + * The maximum value of the slider + */ + public final int maxValue; + + /** + * The initial value of the slider + */ + public final int initValue; + + /** + * Create a new slider settings, with the initial value in the + * middle + * + * @param min + * The minimum value of the slider + * @param max + * The maximum value of the slider + */ + public SliderSettings(final int min, final int max) { + this(min, max, (min + max) / 2); + } + + /** + * Create a new set of slider sttings + * + * @param min + * The minimum slider value + * @param max + * The maximum slider value + * @param init + * Th initial slider value + */ + public SliderSettings(final int min, final int max, final int init) { + minValue = min; + maxValue = max; + + initValue = init; + } + } + + private static final long serialVersionUID = 2956394160569961404L; + private final JSlider slider; + private final JFormattedTextField field; + + /** + * Create a new slider input panel + * + * @param lab + * The label for the field + * @param settings + * The settings for slider values + * @param majorTick + * The setting for where to place big ticks + * @param minorTick + * The setting for where to place small ticks + * @param action + * The action to execute for a given value + */ + public SliderInputPanel(final String lab, final SliderSettings settings, final int majorTick, + final int minorTick, final Consumer action) { + setLayout(new HLayout(3)); + + final JLabel label = new JLabel(lab); + + slider = new JSlider(settings.minValue, settings.maxValue, settings.initValue); + field = new JFormattedTextField(new NumberFormatter(settings)); + + slider.setMajorTickSpacing(majorTick); + slider.setMinorTickSpacing(minorTick); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + + slider.addChangeListener((event) -> { + if (slider.getValueIsAdjusting()) { + // Do nothing + } else { + final int val = slider.getValue(); + + field.setValue(val); + + action.accept(val); + } + }); + + field.setFocusLostBehavior(JFormattedTextField.COMMIT_OR_REVERT); + field.setColumns(15); + field.addPropertyChangeListener("value", (event) -> { + final Object value = field.getValue(); + + if (value == null) { + // Do nothing + } else { + slider.setValue((Integer) value); + } + }); + + add(label); + add(slider); + add(field); + } + + /** + * Reset the values in this panel to a specified value + * + * @param value + * The value to reset the fields to + */ + public void resetValues(final int value) { + slider.setValue(value); + + field.setValue(value); + } +} diff --git a/base/src/main/java/bjc/utils/gui/panels/package-info.java b/base/src/main/java/bjc/utils/gui/panels/package-info.java new file mode 100644 index 0000000..4361885 --- /dev/null +++ b/base/src/main/java/bjc/utils/gui/panels/package-info.java @@ -0,0 +1,5 @@ +/** + * @author ben + * + */ +package bjc.utils.gui.panels; \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/ioutils/CLFormatter.java b/base/src/main/java/bjc/utils/ioutils/CLFormatter.java new file mode 100644 index 0000000..eefd532 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/CLFormatter.java @@ -0,0 +1,531 @@ +package bjc.utils.ioutils; + +import java.util.HashMap; +import java.util.IllegalFormatConversionException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UnknownFormatConversionException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import bjc.utils.PropertyDB; +import bjc.utils.esodata.Tape; +import bjc.utils.esodata.SingleTape; + +import static bjc.utils.PropertyDB.applyFormat; +import static bjc.utils.PropertyDB.getCompiledRegex; +import static bjc.utils.PropertyDB.getRegex; + +public class CLFormatter { + public static class CLModifiers { + public final boolean atMod; + public final boolean colonMod; + + public CLModifiers(boolean at, boolean colon) { + atMod = at; + colonMod = colon; + } + + public static CLModifiers fromString(String modString) { + boolean atMod = false; + boolean colonMod = false; + if(modString != null) { + atMod = modString.contains("@"); + colonMod = modString.contains(":"); + } + + return new CLModifiers(atMod, colonMod); + } + } + + public static class EscapeException extends RuntimeException { + public final boolean endIteration; + + public EscapeException() { + endIteration = false; + } + + public EscapeException(boolean end) { + endIteration = end; + } + } + + @FunctionalInterface + public interface Directive { + /* + * @TODO fill in parameters + */ + public void format(); + } + + private static final String prefixParam = getRegex("clFormatPrefix"); + private static final Pattern pPrefixParam = Pattern.compile(prefixParam); + + private static final String formatMod = getRegex("clFormatModifier"); + + private static final String prefixList = applyFormat("delimSeparatedList", prefixParam, ","); + + private static final String directiveName = getRegex("clFormatName"); + + private static final String formatDirective = applyFormat("clFormatDirective", prefixList, formatMod, directiveName); + private static final Pattern pFormatDirective = Pattern.compile(formatDirective); + + private Map extraDirectives; + + public CLFormatter() { + extraDirectives = new HashMap<>(); + } + + private void checkItem(Object itm, char directive) { + if(itm == null) + throw new IllegalArgumentException(String.format("No argument provided for %c directive", directive)); + } + + public String formatString(String format, Object... params) { + StringBuffer sb = new StringBuffer(); + /* Put the parameters where we can easily handle them. */ + Tape tParams = new SingleTape(params); + + doFormatString(format, sb, tParams); + + return sb.toString(); + } + + private void doFormatString(String format, StringBuffer sb, Tape tParams) { + Matcher dirMatcher = pFormatDirective.matcher(format); + + while(dirMatcher.find()) { + dirMatcher.appendReplacement(sb, ""); + + String dirName = dirMatcher.group("name"); + String dirFunc = dirMatcher.group("funcname"); + String dirMods = dirMatcher.group("modifiers"); + String dirParams = dirMatcher.group("params"); + + CLParameters arrParams = CLParameters.fromDirective(dirParams.split("(?": + /* @TODO + * Figure out how to implement + * tabulation/justification in a + * reasonable manner. + */ + throw new IllegalArgumentException("Layout-control directives aren't implemented yet."); + case "F": + case "E": + case "G": + case "$": + /* @TODO implement floating point directives. */ + throw new IllegalArgumentException("Floating-point directives aren't implemented yet."); + case "S": + case "W": + /* @TODO + * figure out if we want to implement + * someting for these directives instead + * of punting. + * */ + throw new IllegalArgumentException("S and W aren't implemented. Use A instead"); + default: + String msg = String.format("Unknown format directive '%s'", dirName); + throw new UnknownFormatConversionException(msg); + } + } + + dirMatcher.appendTail(sb); + } + + private void handleCDirective(StringBuffer buff, Object parm, CLModifiers mods) { + if(!(parm instanceof Character)) { + throw new IllegalFormatConversionException('C', parm.getClass()); + } + + char ch = (Character) parm; + int codepoint = (int) ch; + + if(mods.colonMod) { + /* + * Colon mod means print Unicode character name. + */ + buff.append(Character.getName(codepoint)); + } else { + buff.append(ch); + } + } + + private void handleFreshlineDirective(StringBuffer buff, CLParameters params) { + int nTimes = 1; + + if(params.length() > 1) { + nTimes = params.getInt(0, "occurance count", '&'); + } + + if(buff.charAt(buff.length() - 1) == '\n') nTimes -= 1; + + for(int i = 0; i < nTimes; i++) { + buff.append("\n"); + } + } + + private void handleLiteralDirective(StringBuffer buff, CLParameters params, String lit, char directive) { + int nTimes = 1; + + if(params.length() > 1) { + nTimes = params.getInt(0, "occurance count", directive); + } + + for(int i = 0; i < nTimes; i++) { + buff.append(lit); + } + } + + private void handleNumberDirective(StringBuffer buff, CLModifiers mods, CLParameters params, int argidx, long val, int radix) { + /* + * Initialize the two padding related parameters, and + * then fill them in from the directive parameters if + * they are present. + */ + int mincol = 0; + char padchar = ' '; + if(params.length() > (argidx + 2)) { + mincol = params.getIntDefault(argidx + 1, "minimum column count", 'R', 0); + } + if(params.length() > (argidx + 3)) { + padchar = params.getCharDefault(argidx + 2, "padding character", 'R', ' '); + } + + if(mods.colonMod) { + /* + * We're doing commas, so check if the two + * comma-related parameters were supplied. + */ + int commaInterval = 0; + char commaChar = ','; + if(params.length() > (argidx + 3)) { + commaChar = params.getCharDefault((argidx + 3), "comma character", 'R', ' '); + } + if(params.length() > (argidx + 4)) { + commaInterval = params.getIntDefault((argidx + 4), "comma interval", 'R', 0); + } + + NumberUtils.toCommaString(val, mincol, padchar, commaInterval, commaChar, mods.atMod, radix); + } else { + NumberUtils.toNormalString(val, mincol, padchar, mods.atMod, radix); + } + } + + private void handleRadixDirective(StringBuffer buff, CLModifiers mods, CLParameters params, Object arg) { + if(!(arg instanceof Number)) { + throw new IllegalFormatConversionException('R', arg.getClass()); + } + + /* + * @TODO see if this is the way we want to do this. + */ + long val = ((Number)arg).longValue(); + + if(params.length() == 0) { + if(mods.atMod) { + buff.append(NumberUtils.toRoman((Long)val, mods.colonMod)); + } else if(mods.colonMod) { + buff.append(NumberUtils.toOrdinal(val)); + } else { + buff.append(NumberUtils.toCardinal(val)); + } + } else { + if(params.length() < 1) + throw new IllegalArgumentException("R directive requires at least one parameter, the radix"); + + int radix = params.getInt(0, "radix", 'R'); + + handleNumberDirective(buff, mods, params, 0, val, radix); + } + } + + private void handleAestheticDirective(StringBuffer buff, Object item, CLModifiers mods, CLParameters params) { + int mincol = 0, colinc = 1, minpad = 0; + char padchar = ' '; + + if(params.length() > 1) { + mincol = params.getIntDefault(0, "minimum column count", 'A', 0); + } + + if(params.length() < 4) { + throw new IllegalArgumentException("Must provide either zero, one or four arguments to A directive"); + } + + colinc = params.getIntDefault(1, "padding increment", 'A', 1); + minpad = params.getIntDefault(2, "minimum amount of padding", 'A', 0); + padchar = params.getCharDefault(3, "padding character", 'A', ' '); + + StringBuilder work = new StringBuilder(); + + if(mods.atMod) { + for(int i = 0; i < minpad; i++) { + work.append(padchar); + } + + for(int i = work.length(); i < mincol; i++) { + for(int k = 0; k < colinc; k++) { + work.append(padchar); + } + } + } + + work.append(item.toString()); + + if(!mods.atMod) { + for(int i = 0; i < minpad; i++) { + work.append(padchar); + } + + for(int i = work.length(); i < mincol; i++) { + for(int k = 0; k < colinc; k++) { + work.append(padchar); + } + } + } + } + + private void handleGotoDirective(CLModifiers mods, CLParameters params, Tape formatParams) { + if(mods.colonMod) { + int num = 1; + if(params.length() > 1) { + num = params.getIntDefault(0, "number of arguments backward", '*', 1); + } + + formatParams.left(num); + } else if(mods.atMod) { + int num = 0; + if(params.length() > 1) { + num = params.getIntDefault(0, "argument index", '*', 0); + } + + formatParams.first(); + formatParams.right(num); + } else { + int num = 1; + if(params.length() > 1) { + num = params.getIntDefault(0, "number of arguments forward", '*', 1); + } + + formatParams.right(num); + } + } + + private void handleConditionalDirective(StringBuffer sb, CLModifiers mods, CLParameters arrParams, Tape formatParams, Matcher dirMatcher) { + StringBuffer condBody = new StringBuffer(); + + List clauses = new ArrayList<>(); + String defClause = null; + boolean isDefault = false; + + while(dirMatcher.find()) { + /* Process a list of clauses. */ + String dirName = dirMatcher.group("name"); + String dirMods = dirMatcher.group("modifiers"); + + if(dirName != null) { + /* Append everything up to this directive. */ + dirMatcher.appendReplacement(condBody, ""); + + if(dirName.equals("]")) { + /* End the conditional. */ + String clause = condBody.toString(); + if(isDefault) { + defClause = clause; + } else { + clauses.add(clause); + } + + break; + } else if(dirName.equals(";")) { + /* End the clause. */ + String clause = condBody.toString(); + if(isDefault) { + defClause = clause; + } else { + clauses.add(clause); + } + + /* Mark the next clause as the default. */ + if(dirMods.contains(":")) { + isDefault = true; + } + } else { + /* Not a special directive. */ + condBody.append(dirMatcher.group()); + } + } + } + + Object par = formatParams.item(); + if(mods.colonMod) { + formatParams.right(); + + if(par == null) { + throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if(!(par instanceof Boolean)) { + throw new IllegalFormatConversionException('[', par.getClass()); + } + boolean res = (Boolean)par; + + String fmt; + if(res) fmt = clauses.get(1); + else fmt = clauses.get(0); + + doFormatString(fmt, sb, formatParams); + } else if(mods.atMod) { + if(par == null) { + throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if(!(par instanceof Boolean)) { + throw new IllegalFormatConversionException('[', par.getClass()); + } + boolean res = (Boolean)par; + + if(res) { + doFormatString(clauses.get(0), sb, formatParams); + } else { + formatParams.right(); + } + } else { + int res; + if(arrParams.length() > 1) { + res = arrParams.getInt(0, "conditional choice", '['); + } else { + if(par == null) { + throw new IllegalArgumentException("No parameter provided for [ directive."); + } else if(!(par instanceof Number)) { + throw new IllegalFormatConversionException('[', par.getClass()); + } + res = ((Number)par).intValue(); + + formatParams.right(); + } + + if(res < 0 || res > clauses.size()) { + if(defClause != null) doFormatString(defClause, sb, formatParams); + } else { + doFormatString(clauses.get(res), sb, formatParams); + } + } + return; + } + + private void handleEscapeDirective(CLModifiers mods, CLParameters params, Tape formatParams) { + boolean shouldExit; + + switch(params.length()) { + case 0: + shouldExit = formatParams.size() == 0; + break; + case 1: + int num = params.getInt(0, "condition count", '^'); + shouldExit = num == 0; + break; + case 2: + int left = params.getInt(0, "left-hand condition", '^'); + int right = params.getInt(1, "right-hand condition", '^'); + shouldExit = left == right; + break; + case 3: + default: + int low = params.getInt(0, "lower-bound condition", '^'); + int mid = params.getInt(1, "interval condition", '^'); + int high = params.getInt(2, "upper-bound condition", '^'); + shouldExit = (low <= mid) && (mid <= high); + break; + } + + /* At negates it. */ + if(mods.atMod) shouldExit = !shouldExit; + + if(shouldExit) throw new EscapeException(mods.colonMod); + } + + +} diff --git a/base/src/main/java/bjc/utils/ioutils/CLParameters.java b/base/src/main/java/bjc/utils/ioutils/CLParameters.java new file mode 100644 index 0000000..e4bb6fb --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/CLParameters.java @@ -0,0 +1,109 @@ +package bjc.utils.ioutils; + +import java.util.ArrayList; +import java.util.List; + +import bjc.utils.esodata.Tape; + +/** + * Represents a set of parameters to a CL format directive. + * + * @author Benjamin Culkin + */ +public class CLParameters { + private String[] params; + + public CLParameters(String[] params) { + this.params = params; + } + + public int length() { + return params.length; + } + + /** + * Creates a set of parameters from an array of parameters. + * + * Mostly, this just fills in V and # parameters. + * + * @param params + * The parameters of the directive. + * @param dirParams + * The parameters of the format string. + * + * @return A set of CL parameters. + */ + public static CLParameters fromDirective(String[] params, Tape dirParams) { + List parameters = new ArrayList<>(); + + for(String param : params) { + if(param.equalsIgnoreCase("V")) { + Object par = dirParams.item(); + boolean succ = dirParams.right(); + + if(par == null) { + throw new IllegalArgumentException("Expected a format parameter for V inline parameter"); + } + + if(par instanceof Number) { + int val = ((Number)par).intValue(); + + parameters.add(Integer.toString(val)); + } else if(par instanceof Character) { + char ch = ((Character)par); + + parameters.add(Character.toString(ch)); + } else { + throw new IllegalArgumentException("Incorrect type of parameter for V inline parameter"); + } + } else if(param.equals("#")) { + parameters.add(Integer.toString(dirParams.position())); + } else { + parameters.add(param); + } + } + + return new CLParameters(parameters.toArray(new String[0])); + } + + public char getCharDefault(int idx, String paramName, char directive, char def) { + if(!params[idx].equals("")) { + return getChar(idx, paramName, directive); + } + + return def; + } + + public char getChar(int idx, String paramName, char directive) { + String param = params[idx]; + + if(!param.startsWith("'")) { + throw new IllegalArgumentException(String.format("Invalid %s %s to %c directive", paramName, param, directive)); + } + + return param.charAt(1); + } + + public int getIntDefault(int idx, String paramName, char directive, int def) { + if(!params[idx].equals("")) { + + } + + return def; + } + + public int getInt(int idx, String paramName, char directive) { + String param = params[idx]; + + try { + return Integer.parseInt(param); + } catch (NumberFormatException nfex) { + String msg = String.format("Invalid %s %s to %c directive", paramName, param, directive); + + IllegalArgumentException iaex = new IllegalArgumentException(msg); + iaex.initCause(nfex); + + throw iaex; + } + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/NumberUtils.java b/base/src/main/java/bjc/utils/ioutils/NumberUtils.java new file mode 100644 index 0000000..1b754e2 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/NumberUtils.java @@ -0,0 +1,405 @@ +package bjc.utils.ioutils; + +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.LongPredicate; + +import static java.util.Map.Entry; + +public class NumberUtils { + /* + * @TODO Use U+305 for large roman numerals, as well as excels 'concise' + * numerals (as implemented by roman()). + */ + public static String toRoman(long number, boolean classic) { + StringBuilder work = new StringBuilder(); + + long currNumber = number; + + if(currNumber == 0) { + return "N"; + } + + if(currNumber < 0) { + currNumber *= -1; + + work.append("-"); + } + + if(currNumber >= 1000) { + int numM = (int)(currNumber / 1000); + currNumber = currNumber % 1000; + + for(int i = 0; i < numM; i++) { + work.append("M"); + } + } + + if(currNumber >= 900 && !classic) { + currNumber = currNumber % 900; + + work.append("CM"); + } + + if(currNumber >= 500) { + currNumber = currNumber % 500; + + work.append("D"); + } + + if(currNumber >= 400 && !classic) { + currNumber = currNumber % 400; + + work.append("CD"); + } + + if(currNumber >= 100) { + int numC = (int)(currNumber / 100); + currNumber = currNumber % 100; + + for(int i = 0; i < numC; i++) { + work.append("C"); + } + } + + if(currNumber >= 90 && !classic) { + currNumber = currNumber % 90; + + work.append("XC"); + } + + if(currNumber >= 50) { + currNumber = currNumber % 50; + + work.append("L"); + } + + if(currNumber >= 40 && !classic) { + currNumber = currNumber % 40; + + work.append("XL"); + } + + if(currNumber >= 10) { + int numX = (int)(currNumber / 10); + currNumber = currNumber % 10; + + for(int i = 0; i < numX; i++) { + work.append("X"); + } + } + + if(currNumber >= 9 && !classic) { + currNumber = currNumber % 9; + + work.append("IX"); + } + + if(currNumber >= 5) { + currNumber = currNumber % 5; + + work.append("V"); + } + + if(currNumber >= 4 && !classic) { + currNumber = currNumber % 4; + + work.append("IV"); + } + + if(currNumber >= 1) { + int numI = (int)(currNumber / 1); + currNumber = currNumber % 1; + + for(int i = 0; i < numI; i++) { + work.append("I"); + } + } + + return work.toString(); + } + + public static String toCardinal(long number) { + return toCardinal(number, null); + } + + private static String[] cardinals = new String[] { + "zero", "one", "two", "three", "four", "five", "six", "seven", + "eight", "nine", "ten", "eleven", "twelve", "thirteen", + "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", + "nineteen", "twenty", + }; + + public static class CardinalState { + public final Map customNumbers; + public final Map> customScales; + + public CardinalState(Map customNumbers, Map> customScales) { + this.customNumbers = customNumbers; + this.customScales = customScales; + } + + public String handleCustom(long number) { + if(customNumbers.containsKey(number)) { + return customNumbers.get(number); + } + + for(Entry> ent : customScales.entrySet()) { + if(ent.getKey().test(number)) { + return ent.getValue().apply(number, this); + } + } + + return null; + } + } + + public static String toCardinal(long number, CardinalState custom) { + if(custom != null) { + String res = custom.handleCustom(number); + + if(res != null) return res; + } + + if(number < 0) return "negative " + toCardinal(number * -1, custom); + + if(number <= 20) return cardinals[(int)number]; + + if(number < 100) { + if(number % 10 == 0) { + switch((int)number) { + case 30: + return "thirty"; + case 40: + return "forty"; + case 50: + return "fifty"; + case 60: + return "sixty"; + case 70: + return "seventy"; + case 80: + return "eighty"; + case 90: + return "ninety"; + default: + /* + * Shouldn't happen. + */ + assert(false); + } + } + + long numTens = (long)(number / 10); + long numOnes = number % 10; + + return toCardinal(numTens, custom) + "-" + toCardinal(numOnes, custom); + } + + if(number < 1000) { + long numHundreds = (long)(number / 100); + long rest = number % 100; + + return toCardinal(numHundreds, custom) + " hundred and " + toCardinal(rest, custom); + } + + long MILLION = (long)(Math.pow(10, 6)); + if(number < MILLION) { + long numThousands = (long)(number / 1000); + long rest = number % 1000; + + return toCardinal(numThousands, custom) + " thousand, " + toCardinal(rest, custom); + } + + long BILLION = (long)(Math.pow(10, 9)); + if(number < BILLION) { + long numMillions = (long)(number / MILLION); + long rest = number % MILLION; + + return toCardinal(numMillions, custom) + " million, " + toCardinal(rest, custom); + } + + long TRILLION = (long)(Math.pow(10, 12)); + if(number < TRILLION) { + long numBillions = (long)(number / BILLION); + long rest = number % BILLION; + + return toCardinal(numBillions, custom) + " billion, " + toCardinal(rest, custom); + } + + throw new IllegalArgumentException("Numbers greater than or equal to 1 trillion are not supported yet."); + } + + public static String toOrdinal(long number) { + if(number < 0) { + return "minus " + toOrdinal(number); + } + + if(number < 20) { + switch((int)number) { + case 0: + return "zeroth"; + case 1: + return "first"; + case 2: + return "second"; + case 3: + return "third"; + case 4: + return "fourth"; + case 5: + return "fifth"; + case 6: + return "sixth"; + case 7: + return "seventh"; + case 8: + return "eighth"; + case 9: + return "ninth"; + case 10: + return "tenth"; + case 11: + return "eleventh"; + case 12: + return "twelfth"; + case 13: + return "thirteenth"; + case 14: + return "fourteenth"; + case 15: + return "fifteenth"; + case 16: + return "sixteenth"; + case 17: + return "seventeenth"; + case 18: + return "eighteenth"; + case 19: + return "nineteenth"; + default: + /* + * Shouldn't happen. + */ + assert(false); + } + } + + if(number < 100) { + if(number % 10 == 0) { + switch((int)number) { + case 20: + return "twentieth"; + case 30: + return "thirtieth"; + case 40: + return "fortieth"; + case 50: + return "fiftieth"; + case 60: + return "sixtieth"; + case 70: + return "seventieth"; + case 80: + return "eightieth"; + case 90: + return "ninetieth"; + } + } + + long numPostfix = number % 10; + return toCardinal(number - numPostfix) + "-" + toOrdinal(numPostfix); + } + + long procNum = number % 100; + long tens = (long)(procNum / 10); + long ones = procNum % 10; + + if(tens == 1) { + return Long.toString(number) + "th"; + } + + switch((int)ones) { + case 1: + return Long.toString(number) + "st"; + case 2: + return Long.toString(number) + "nd"; + case 3: + return Long.toString(number) + "rd"; + default: + return Long.toString(number) + "th"; + } + } + + private static char[] radixChars = new char[62]; + static { + int idx = 0; + + for(char i = 0; i < 10; i++) { + radixChars[idx] = (char)('0' + i); + + idx += 1; + } + + for(char i = 0; i < 26; i++) { + radixChars[idx] = (char)('A' + i); + + idx += 1; + } + + for(char i = 0; i < 26; i++) { + radixChars[idx] = (char)('a' + i); + + idx += 1; + } + } + + public static String toCommaString(long val, int mincols, char padchar, int commaInterval, char commaChar, boolean signed, int radix) { + if(radix > radixChars.length) { + throw new IllegalArgumentException(String.format("Radix %d is larger than largest supported radix %d", radix, radixChars.length)); + } + + StringBuilder work = new StringBuilder(); + + boolean isNeg = false; + long currVal = val; + if(currVal < 0) { + isNeg = true; + currVal *= -1; + } + + if(currVal == 0) { + work.append(radixChars[0]); + } else { + int valCounter = 0; + + while(currVal != 0) { + valCounter += 1; + + int radDigit = (int)(currVal % radix); + work.append(radixChars[radDigit]); + currVal = (long)(currVal / radix); + + if(commaInterval != 0 && valCounter % commaInterval == 0) work.append(commaChar); + } + } + + if(isNeg) work.append("-"); + else if(signed) work.append("+"); + + work.reverse(); + + /* @TODO Should we have some way to specify how to pad? */ + if(work.length() < mincols) { + for(int i = work.length(); i < mincols; i++) { + work.append(padchar); + } + } + + return work.toString(); + } + + public static String toNormalString(long val, int mincols, char padchar, boolean signed, int radix) { + return toCommaString(val, mincols, padchar, 0, ',', signed, radix); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/Prompter.java b/base/src/main/java/bjc/utils/ioutils/Prompter.java new file mode 100644 index 0000000..a6ec4c0 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/Prompter.java @@ -0,0 +1,47 @@ +package bjc.utils.ioutils; + +import java.io.PrintStream; + +import bjc.utils.ioutils.blocks.TriggeredBlockReader; + +/** + * A runnable for use with {@link TriggeredBlockReader} to prompt the user for + * input. + * + * @author bjculkin + * + */ +public final class Prompter implements Runnable { + private String promt; + private final PrintStream printer; + + /** + * Create a new prompter using the specified prompt. + * + * @param prompt + * The prompt to present. + * + * @param output + * The stream to print the prompt on. + */ + public Prompter(final String prompt, final PrintStream output) { + promt = prompt; + + printer = output; + } + + /** + * Set the prompt this prompter uses. + * + * @param prompt + * The prompt this prompter uses. + */ + public void setPrompt(final String prompt) { + promt = prompt; + } + + @Override + public void run() { + printer.print(promt); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/ioutils/RegexStringEditor.java b/base/src/main/java/bjc/utils/ioutils/RegexStringEditor.java new file mode 100644 index 0000000..71f6782 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/RegexStringEditor.java @@ -0,0 +1,230 @@ +package bjc.utils.ioutils; + +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import bjc.utils.data.Toggle; +import bjc.utils.data.ValueToggle; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; +import bjc.utils.functypes.ID; + +/** + * Editor methods for strings based off the command language for the Sam editor. + * + * @author EVE + * + */ +public class RegexStringEditor { + private static final UnaryOperator SID = ID.id(); + + /** + * Replace every occurrence of the pattern with the result of applying + * the action to the string matched by the pattern. + * + * @param input + * The input string to process. + * + * @param patt + * The pattern to match the string against. + * + * @param action + * The action to transform matches with. + * + * @return The string, with matches replaced with the action. + */ + public static String onOccurances(final String input, final Pattern patt, final UnaryOperator action) { + return reduceOccurances(input, patt, SID, action); + } + + /** + * Replace every occurrence between the patterns with the result of + * applying the action to the strings between the patterns. + * + * @param input + * The input string to process. + * + * @param patt + * The pattern to match the string against. + * + * @param action + * The action to transform matches with. + * + * @return The string, with strings between the matches replaced with + * the action. + */ + public static String betweenOccurances(final String input, final Pattern patt, + final UnaryOperator action) { + return reduceOccurances(input, patt, action, SID); + } + + /** + * Execute actions between and on matches of a regular expression. + * + * @param input + * The input string. + * + * @param rPatt + * The pattern to match against the string. + * + * @param betweenAction + * The function to execute between matches of the string. + * + * @param onAction + * The function to execute on matches of the string. + * + * @return The string, with both actions applied. + */ + public static String reduceOccurances(final String input, final Pattern rPatt, + final UnaryOperator betweenAction, final UnaryOperator onAction) { + /* + * Get all of the occurances. + */ + final IList occurances = listOccurances(input, rPatt); + + /* + * Execute the correct action on every occurance. + */ + final Toggle> actions = new ValueToggle<>(onAction, betweenAction); + final BiFunction reducer = (strang, state) -> { + return state.append(actions.get().apply(strang)); + }; + + /* + * Convert the list back to a string. + */ + return occurances.reduceAux(new StringBuilder(), reducer, StringBuilder::toString); + } + + /** + * Execute actions between and on matches of a regular expression. + * + * @param input + * The input string. + * + * @param rPatt + * The pattern to match against the string. + * + * @param betweenAction + * The function to execute between matches of the string. + * + * @param onAction + * The function to execute on matches of the string. + * + * @return The string, with both actions applied. + */ + public static IList mapOccurances(final String input, final Pattern rPatt, + final UnaryOperator betweenAction, final UnaryOperator onAction) { + /* + * Get all of the occurances. + */ + final IList occurances = listOccurances(input, rPatt); + + /* + * Execute the correct action on every occurance. + */ + final Toggle> actions = new ValueToggle<>(onAction, betweenAction); + return occurances.map(strang -> actions.get().apply(strang)); + } + + /** + * Separate a string into match/non-match segments. + * + * @param input + * The string to separate. + * + * @param rPatt + * The pattern to use for separation. + * + * @return The string, as a list of match/non-match segments, + * starting/ending with a non-match segment. + */ + public static IList listOccurances(final String input, final Pattern rPatt) { + final IList res = new FunctionalList<>(); + + /* + * Create the matcher and work buffer. + */ + final Matcher matcher = rPatt.matcher(input); + StringBuffer work = new StringBuffer(); + + /* + * For every match. + */ + while (matcher.find()) { + final String match = matcher.group(); + + /* + * Append the text until the match to the buffer. + */ + matcher.appendReplacement(work, ""); + + res.add(work.toString()); + res.add(match); + + /* + * Clear the buffer. + */ + work = new StringBuffer(); + } + + /* + * Add the text after the last match to the buffer. + */ + matcher.appendTail(work); + res.add(work.toString()); + + return res; + } + + /** + * Apply an operation to a string if it matches a regular expression. + * + * @param input + * The input string. + * + * @param patt + * The pattern to match against it. + * + * @param action + * The action to execute if it matches. + * + * @return The string, modified by the action if the pattern matched. + */ + public static String ifMatches(final String input, final Pattern patt, final UnaryOperator action) { + final Matcher matcher = patt.matcher(input); + + if (matcher.matches()) { + return action.apply(input); + } else { + return input; + } + } + + /** + * Apply an operation to a string if it matches a regular expression. + * + * @param input + * The input string. + * + * @param patt + * The pattern to match against it. + * + * @param action + * The action to execute if it doesn't match. + * + * @return The string, modified by the action if the pattern didn't + * match. + */ + public static String ifNotMatches(final String input, final Pattern patt, final UnaryOperator action) { + final Matcher matcher = patt.matcher(input); + + if (matcher.matches()) { + return input; + } else { + return action.apply(input); + } + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java b/base/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java new file mode 100644 index 0000000..7c5205b --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java @@ -0,0 +1,265 @@ +package bjc.utils.ioutils; + +import java.io.InputStream; +import java.util.InputMismatchException; +import java.util.Scanner; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import bjc.utils.data.IHolder; +import bjc.utils.data.IPair; +import bjc.utils.data.Identity; +import bjc.utils.data.Pair; +import bjc.utils.exceptions.UnknownPragmaException; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.FunctionalStringTokenizer; +import bjc.utils.funcdata.IMap; + +/** + * This class parses a rules based config file, and uses it to drive a provided + * set of actions + * + * @author ben + * + * @param + * The type of the state object to use + * + */ +public class RuleBasedConfigReader { + /* Function to execute when starting a rule. + * Takes the tokenizer, and a pair of the read token and application state + */ + private BiConsumer> start; + + /* + * Function to use when continuing a rule + * Takes a tokenizer and application state + */ + private BiConsumer continueRule; + + /* + * Function to use when ending a rule + * Takes an application state + */ + private Consumer end; + + /* + * Map of pragma names to pragma actions + * Pragma actions are functions taking a tokenizer and application state + */ + private final IMap> pragmas; + + /** + * Create a new rule-based config reader + * + * @param start + * The action to fire when starting a rule + * @param continueRule + * The action to fire when continuing a rule + * @param end + * The action to fire when ending a rule + */ + public RuleBasedConfigReader(final BiConsumer> start, + final BiConsumer continueRule, final Consumer end) { + this.start = start; + this.continueRule = continueRule; + this.end = end; + + this.pragmas = new FunctionalMap<>(); + } + + /** + * Add a pragma to this reader + * + * @param name + * The name of the pragma to add + * @param action + * The function to execute when this pragma is read + */ + public void addPragma(final String name, final BiConsumer action) { + if (name == null) throw new NullPointerException("Pragma name must not be null"); + else if (action == null) throw new NullPointerException("Pragma action must not be null"); + + pragmas.put(name, action); + } + + private void continueRule(final E state, final boolean isRuleOpen, final String line) { + // Make sure our input is correct + if (isRuleOpen == false) + throw new InputMismatchException("Cannot continue rule with no rule open"); + else if (continueRule == null) + throw new InputMismatchException("Rule continuation not supported for current grammar"); + + /* + * Accept the rule + */ + continueRule.accept(new FunctionalStringTokenizer(line.substring(1), " "), state); + } + + private boolean endRule(final E state, final boolean isRuleOpen) { + /* + * Ignore blank line without an open rule + */ + if (isRuleOpen == false) + /* + * Do nothing + */ + return false; + else { + /* + * Nothing happens on rule end + */ + if (end != null) { + /* + * Process the rule ending + */ + end.accept(state); + } + + /* + * Return a closed rule + */ + return false; + } + } + + /** + * Run a stream through this reader + * + * @param input + * The stream to get input + * @param initialState + * The initial state of the reader + * @return The final state of the reader + */ + public E fromStream(final InputStream input, final E initialState) { + if (input == null) throw new NullPointerException("Input stream must not be null"); + + /* + * Application state: We're giving this back later + */ + final E state = initialState; + + /* + * Prepare our input source + */ + try (Scanner source = new Scanner(input)) { + source.useDelimiter("\n"); + /* + * This is true when a rule's open + */ + final IHolder isRuleOpen = new Identity<>(false); + + /* + * Do something for every line of the file + */ + source.forEachRemaining((line) -> { + /* + * Skip comment lines + */ + if (line.startsWith("#") || line.startsWith("//")) + /* + * It's a comment + */ + return; + else if (line.equals("")) { + /* + * End the rule + */ + isRuleOpen.replace(endRule(state, isRuleOpen.getValue())); + } else if (line.startsWith("\t")) { + /* + * Continue the rule + */ + continueRule(state, isRuleOpen.getValue(), line); + } else { + /* + * Open a rule + */ + isRuleOpen.replace(startRule(state, isRuleOpen.getValue(), line)); + } + }); + } + + /* + * Return the state that the user has created + */ + return state; + } + + /** + * Set the action to execute when continuing a rule + * + * @param continueRule + * The action to execute on continuation of a rule + */ + public void setContinueRule(final BiConsumer continueRule) { + this.continueRule = continueRule; + } + + /** + * Set the action to execute when ending a rule + * + * @param end + * The action to execute on ending of a rule + */ + public void setEndRule(final Consumer end) { + this.end = end; + } + + /** + * Set the action to execute when starting a rule + * + * @param start + * The action to execute on starting of a rule + */ + public void setStartRule(final BiConsumer> start) { + if (start == null) throw new NullPointerException("Action on rule start must be non-null"); + + this.start = start; + } + + private boolean startRule(final E state, boolean isRuleOpen, final String line) { + /* + * Create the line tokenizer + */ + final FunctionalStringTokenizer tokenizer = new FunctionalStringTokenizer(line, " "); + + /* + * Get the initial token + */ + final String nextToken = tokenizer.nextToken(); + + /* + * Handle pragmas + */ + if (nextToken.equals("pragma")) { + /* + * Get the pragma name + */ + final String token = tokenizer.nextToken(); + + /* + * Handle pragmas + */ + pragmas.getOrDefault(token, (tokenzer, stat) -> { + throw new UnknownPragmaException("Unknown pragma " + token); + }).accept(tokenizer, state); + } else { + /* + * Make sure input is correct + */ + if (isRuleOpen == true) + throw new InputMismatchException("Nested rules are currently not supported"); + + /* + * Start a rule + */ + start.accept(tokenizer, new Pair<>(nextToken, state)); + + isRuleOpen = true; + } + + return isRuleOpen; + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java b/base/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java new file mode 100644 index 0000000..e26a7ee --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java @@ -0,0 +1,100 @@ +package bjc.utils.ioutils; + +import java.util.function.BiConsumer; + +import bjc.utils.exceptions.PragmaFormatException; +import bjc.utils.funcdata.FunctionalStringTokenizer; +import bjc.utils.funcutils.ListUtils; + +/** + * Contains factory methods for common pragma types + * + * @author ben + * + */ +public class RuleBasedReaderPragmas { + + /** + * Creates a pragma that takes a single integer argument + * + * @param + * The type of state that goes along with this pragma + * @param name + * The name of this pragma, for error message purpose + * @param consumer + * The function to invoke with the parsed integer + * @return A pragma that functions as described above. + */ + public static BiConsumer buildInteger(final String name, + final BiConsumer consumer) { + return (tokenizer, state) -> { + /* + * Check our input is correct + */ + if (!tokenizer.hasMoreTokens()) { + String fmt = "Pragma %s requires one integer argument"; + + throw new PragmaFormatException(String.format(fmt, name)); + } + + /* + * Read the argument + */ + final String token = tokenizer.nextToken(); + + try { + /* + * Run the pragma + */ + consumer.accept(Integer.parseInt(token), state); + } catch (final NumberFormatException nfex) { + /* + * Tell the user their argument isn't correct + */ + String fmt = "Argument %s to %s pragma isn't a valid integer, and this pragma requires an integer argument."; + + final PragmaFormatException pfex = new PragmaFormatException(String.format(fmt, token, name)); + + pfex.initCause(nfex); + + throw pfex; + } + }; + } + + /** + * Creates a pragma that takes any number of arguments and collapses + * them all into a single string + * + * @param + * The type of state that goes along with this pragma + * @param name + * The name of this pragma, for error message purpose + * @param consumer + * The function to invoke with the parsed string + * @return A pragma that functions as described above. + */ + public static BiConsumer buildStringCollapser( + final String name, final BiConsumer consumer) { + return (tokenizer, state) -> { + /* + * Check our input + */ + if (!tokenizer.hasMoreTokens()) { + String fmt = "Pragma %s requires one or more string arguments."; + + throw new PragmaFormatException(String.format(fmt, name)); + } + + /* + * Build our argument + */ + final String collapsed = ListUtils.collapseTokens(tokenizer.toList()); + + /* + * Run the pragma + */ + consumer.accept(collapsed, state); + }; + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/SimpleProperties.java b/base/src/main/java/bjc/utils/ioutils/SimpleProperties.java new file mode 100644 index 0000000..e6279c4 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/SimpleProperties.java @@ -0,0 +1,170 @@ +package bjc.utils.ioutils; + +import java.io.InputStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Scanner; +import java.util.Set; + +/** + * Simple file based properties. + * + * @author EVE + * + */ +public class SimpleProperties implements Map { + private final Map props; + + /** + * Create a new set of simple properties. + */ + public SimpleProperties() { + props = new HashMap<>(); + } + + /** + * Load properties from the provided input stream. + * + * The format is the name, a space, then the body. + * + * All leading/trailing spaces from the name & body are removed. + * + * @param is + * The stream to read from. + * + * @param allowDuplicates + * Whether or not duplicate keys should be allowed. + */ + public void loadFrom(final InputStream is, final boolean allowDuplicates) { + try (Scanner scn = new Scanner(is)) { + while (scn.hasNextLine()) { + final String ln = scn.nextLine().trim(); + + /* + * Skip blank lines/comments + */ + if (ln.equals("")) { + continue; + } + if (ln.startsWith("#")) { + continue; + } + + final int sepIdx = ln.indexOf(' '); + + /* + * Complain about improperly formatted lines. + */ + if (sepIdx == -1) { + final String fmt = "Properties must be a name, a space, then the body.\n\tOffending line is '%s'"; + final String msg = String.format(fmt, ln); + + throw new NoSuchElementException(msg); + } + + final String name = ln.substring(0, sepIdx).trim(); + final String body = ln.substring(sepIdx).trim(); + + /* + * Complain about duplicates, if that is wanted. + */ + if (!allowDuplicates && containsKey(name)) { + final String msg = String.format("Duplicate key '%s'", name); + + throw new IllegalStateException(msg); + } + + put(name, body); + } + } + } + + /** + * Output the set of read properties. + */ + public void outputProperties() { + System.out.println("Read properties:"); + + for (final Entry entry : entrySet()) { + System.out.printf("\t'%s'\t'%s'\n", entry.getKey(), entry.getValue()); + } + + System.out.println(); + } + + @Override + public int size() { + return props.size(); + } + + @Override + public boolean isEmpty() { + return props.isEmpty(); + } + + @SuppressWarnings("unlikely-arg-type") + @Override + public boolean containsKey(final Object key) { + return props.containsKey(key); + } + + @SuppressWarnings("unlikely-arg-type") + @Override + public boolean containsValue(final Object value) { + return props.containsValue(value); + } + + @SuppressWarnings("unlikely-arg-type") + @Override + public String get(final Object key) { + return props.get(key); + } + + @Override + public String put(final String key, final String value) { + return props.put(key, value); + } + + @SuppressWarnings("unlikely-arg-type") + @Override + public String remove(final Object key) { + return props.remove(key); + } + + @Override + public void putAll(final Map m) { + props.putAll(m); + } + + @Override + public void clear() { + props.clear(); + } + + @Override + public Set keySet() { + return props.keySet(); + } + + @Override + public Collection values() { + return props.values(); + } + + @Override + public Set> entrySet() { + return props.entrySet(); + } + + @Override + public boolean equals(final Object o) { + return props.equals(o); + } + + @Override + public int hashCode() { + return props.hashCode(); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/Block.java b/base/src/main/java/bjc/utils/ioutils/blocks/Block.java new file mode 100644 index 0000000..15f3510 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/Block.java @@ -0,0 +1,88 @@ +package bjc.utils.ioutils.blocks; + +/** + * Represents a block of text read in from a source. + * + * @author EVE + * + */ +public class Block { + /** + * The contents of this block. + */ + public final String contents; + + /** + * The line of the source this block started on. + */ + public final int startLine; + + /** + * The line of the source this block ended on. + */ + public final int endLine; + + /** + * The number of this block. + */ + public final int blockNo; + + /** + * Create a new block. + * + * @param blockNo + * The number of this block. + * @param contents + * The contents of this block. + * @param startLine + * The line this block started on. + * @param endLine + * The line this block ended. + */ + public Block(final int blockNo, final String contents, final int startLine, final int endLine) { + this.contents = contents; + this.startLine = startLine; + this.endLine = endLine; + this.blockNo = blockNo; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + blockNo; + result = prime * result + (contents == null ? 0 : contents.hashCode()); + result = prime * result + endLine; + result = prime * result + startLine; + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof Block)) return false; + + final Block other = (Block) obj; + + if (blockNo != other.blockNo) return false; + + if (contents == null) { + if (other.contents != null) return false; + } else if (!contents.equals(other.contents)) return false; + + if (endLine != other.endLine) return false; + if (startLine != other.startLine) return false; + + return true; + } + + @Override + public String toString() { + String fmt = "Block #%d (from lines %d to %d), length: %d characters"; + + return String.format(fmt, blockNo, startLine, endLine, contents.length()); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java b/base/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java new file mode 100644 index 0000000..3c695c6 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java @@ -0,0 +1,73 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; +import java.util.Iterator; +import java.util.function.Consumer; + +/** + * A source of blocks of characters, marked with line numbers as to block + * start/block end. + * + * @author bjculkin + * + */ +public interface BlockReader extends AutoCloseable, Iterator { + /** + * Check if this reader has an available block. + * + * @return Whether or not another block is available. + */ + boolean hasNextBlock(); + + /** + * Get the current block. + * + * @return The current block, or null if there is no current block. + */ + Block getBlock(); + + /** + * Move to the next block. + * + * @return Whether or not the next block was successfully read. + */ + boolean nextBlock(); + + /** + * Retrieve the number of blocks that have been read so far. + * + * @return The number of blocks read so far. + */ + int getBlockCount(); + + @Override + void close() throws IOException; + + /* + * Methods with default impls. + */ + + /** + * Execute an action for each remaining block. + * + * @param action + * The action to execute for each block + */ + default void forEachBlock(final Consumer action) { + while (hasNext()) { + action.accept(next()); + } + } + + @Override + default boolean hasNext() { + return hasNextBlock(); + } + + @Override + default Block next() { + nextBlock(); + + return getBlock(); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java b/base/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java new file mode 100644 index 0000000..8bbb89c --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java @@ -0,0 +1,81 @@ +package bjc.utils.ioutils.blocks; + +import java.io.Reader; + +/** + * Utility methods for constructing instances of {@link BlockReader} + * + * @author bjculkin + * + */ +public class BlockReaders { + /** + * Create a new simple block reader that works off a regex. + * + * @param blockDelim + * The regex that separates blocks. + * + * @param source + * The reader to get blocks from. + * + * @return A configured simple reader. + */ + public static SimpleBlockReader simple(final String blockDelim, final Reader source) { + return new SimpleBlockReader(blockDelim, source); + } + + /** + * Create a new pushback block reader. + * + * @param src + * The block reader to read blocks from. + * + * @return A configured pushback reader. + */ + public static PushbackBlockReader pushback(final BlockReader src) { + return new PushbackBlockReader(src); + } + + /** + * Create a new triggered block reader. + * + * @param source + * The block reader to read blocks from. + * + * @param action + * The action to execute before reading a block. + * + * @return A configured triggered block reader. + */ + public static BlockReader trigger(final BlockReader source, final Runnable action) { + return new TriggeredBlockReader(source, action); + } + + /** + * Create a new layered block reader. + * + * @param primary + * The first source to read blocks from. + * + * @param secondary + * The second source to read blocks from. + * + * @return A configured layered block reader. + */ + public static BlockReader layered(final BlockReader primary, final BlockReader secondary) { + return new LayeredBlockReader(primary, secondary); + } + + /** + * Create a new serial block reader. + * + * @param readers + * The readers to pull from, in the order to pull from + * them. + * + * @return A configured serial block reader. + */ + public static BlockReader serial(final BlockReader... readers) { + return new SerialBlockReader(readers); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/BoundBlockReader.java b/base/src/main/java/bjc/utils/ioutils/blocks/BoundBlockReader.java new file mode 100644 index 0000000..b1e82d7 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/BoundBlockReader.java @@ -0,0 +1,61 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; + +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; + +public class BoundBlockReader implements BlockReader { + @FunctionalInterface + public interface Closer { + public void close() throws IOException; + } + + private BooleanSupplier checker; + private Supplier getter; + private Closer closer; + + private Block current; + + private int blockNo; + + public BoundBlockReader(BooleanSupplier blockChecker, Supplier blockGetter, Closer blockCloser) { + checker = blockChecker; + getter = blockGetter; + closer = blockCloser; + + blockNo = 0; + } + + @Override + public boolean hasNextBlock() { + return checker.getAsBoolean(); + } + + @Override + public Block getBlock() { + return current; + } + + @Override + public boolean nextBlock() { + if(checker.getAsBoolean()) { + current = getter.get(); + blockNo += 1; + + return true; + } + + return false; + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + closer.close(); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java b/base/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java new file mode 100644 index 0000000..0b43f7a --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java @@ -0,0 +1,97 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; + +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class FilteredBlockReader implements BlockReader { + /* + * The source of blocks. + */ + private BlockReader source; + + /* + * The current and next block. + * + * Both have already been checked for the predicate. + */ + private Block current; + private Block pending; + + /* + * Number of blocks that passed the predicate. + */ + private int blockNo; + + /* + * The predicate blocks must pass. + */ + private Predicate pred; + + /* + * The action to call on failure, if there is one. + */ + private Consumer failAction; + + public FilteredBlockReader(BlockReader src, Predicate predic) { + this(src, predic, null); + } + + public FilteredBlockReader(BlockReader src, Predicate predic, Consumer failAct) { + source = src; + pred = predic; + failAction = failAct; + + blockNo = 0; + } + + @Override + public boolean hasNextBlock() { + if(pending != null) return true; + + while(source.hasNextBlock()) { + /* + * Only say we have a next block if the next block would + * pass the predicate. + */ + pending = source.next(); + + if(pred.test(pending)) { + blockNo += 1; + return true; + } else { + failAction.accept(pending); + } + } + + return false; + } + + @Override + public Block getBlock() { + return current; + } + + @Override + public boolean nextBlock() { + if(pending != null || hasNextBlock()) { + current = pending; + pending = null; + + return true; + } + + return false; + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + source.close(); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/FlatMappedBlockReader.java b/base/src/main/java/bjc/utils/ioutils/blocks/FlatMappedBlockReader.java new file mode 100644 index 0000000..f4d8439 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/FlatMappedBlockReader.java @@ -0,0 +1,86 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +/** + * A block reader that supports applying a flatmap operation to blocks. + * + * The use-case in mind for this was tokenizing blocks. + * + * @author Benjamin Culkin + */ +public class FlatMappedBlockReader implements BlockReader { + /* + * The source reader. + */ + private BlockReader reader; + + /* + * The current block, and any blocks pending from the last source block. + */ + private Iterator pending; + private Block current; + + /* + * The operator to open blocks with. + */ + private Function> transform; + + /* + * The current block number. + */ + private int blockNo; + + public FlatMappedBlockReader(BlockReader source, Function> trans) { + reader = source; + transform = trans; + + blockNo = 0; + } + + @Override + public boolean hasNextBlock() { + return pending.hasNext() || reader.hasNextBlock(); + } + + @Override + public Block getBlock() { + return current; + } + + @Override + public boolean nextBlock() { + /* + * Attempt to get a new pending list if the one we have isn't + * valid. + */ + while(pending == null || !pending.hasNext()) { + if(!reader.hasNext()) return false; + + pending = transform.apply(reader.next()).iterator(); + } + + /* + * Advance the iterator. + */ + current = pending.next(); + blockNo += 1; + + return true; + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + reader.close(); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java b/base/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java new file mode 100644 index 0000000..967a1f2 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java @@ -0,0 +1,81 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; + +/** + * A block reader that supports draining all the blocks from one reading before + * swapping to another. + * + * This is more a 'prioritize blocks from one over the other', than a 'read all + * the blocks from one, then all the blocks from the other'. If you need that, + * look at {@link SerialBlockReader}. + * + * @author bjculkin + * + */ +public class LayeredBlockReader implements BlockReader { + /* + * The readers to drain from. + */ + private final BlockReader first; + private final BlockReader second; + + /* + * The current block number. + */ + private int blockNo; + + /** + * Create a new layered block reader. + * + * @param primary + * The first source to read blocks from. + * + * @param secondary + * The second source to read blocks from. + */ + public LayeredBlockReader(final BlockReader primary, final BlockReader secondary) { + first = primary; + second = secondary; + } + + @Override + public boolean hasNextBlock() { + return first.hasNextBlock() || second.hasNextBlock(); + } + + @Override + public Block getBlock() { + final Block firstBlock = first.getBlock(); + + /* + * Only drain a block from the second reader if none are + * available in the first reader. + */ + return firstBlock == null ? second.getBlock() : firstBlock; + } + + @Override + public boolean nextBlock() { + final boolean gotFirst = first.nextBlock(); + final boolean succ = gotFirst ? gotFirst : second.nextBlock(); + + if (succ) { + blockNo += 1; + } + + return succ; + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + second.close(); + + first.close(); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java b/base/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java new file mode 100644 index 0000000..12fa848 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java @@ -0,0 +1,54 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; + +import java.util.function.UnaryOperator; + +public class MappedBlockReader implements BlockReader { + private BlockReader reader; + + private Block current; + + private UnaryOperator transform; + + private int blockNo; + + public MappedBlockReader(BlockReader source, UnaryOperator trans) { + reader = source; + transform = trans; + + blockNo = 0; + } + + @Override + public boolean hasNextBlock() { + return reader.hasNextBlock(); + } + + @Override + public Block getBlock() { + return current; + } + + @Override + public boolean nextBlock() { + if(hasNextBlock()) { + current = transform.apply(reader.next()); + blockNo += 1; + + return true; + } + + return false; + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + reader.close(); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java b/base/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java new file mode 100644 index 0000000..0cc9dea --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java @@ -0,0 +1,106 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; +import java.util.Deque; +import java.util.LinkedList; + +/** + * A block reader that supports pushing blocks onto the input queue so that they + * are provided before blocks read from an input source. + * + * @author bjculkin + * + */ +public class PushbackBlockReader implements BlockReader { + private final BlockReader source; + + /* + * The queue of pushed-back blocks. + */ + private final Deque waiting; + + private Block curBlock; + + private int blockNo; + + /** + * Create a new pushback block reader. + * + * @param src + * The block reader to use when no blocks are queued. + */ + public PushbackBlockReader(final BlockReader src) { + source = src; + + waiting = new LinkedList<>(); + } + + @Override + public boolean hasNextBlock() { + return !waiting.isEmpty() || source.hasNextBlock(); + } + + @Override + public Block getBlock() { + return curBlock; + } + + @Override + public boolean nextBlock() { + /* + * Drain pushed-back blocks first. + */ + if (!waiting.isEmpty()) { + curBlock = waiting.pop(); + + blockNo += 1; + + return true; + } else { + final boolean succ = source.nextBlock(); + curBlock = source.getBlock(); + + if (succ) { + blockNo += 1; + } + + return succ; + } + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + source.close(); + } + + /** + * Insert a block at the back of the queue of pending blocks. + * + * @param blk + * The block to put at the back. + */ + public void addBlock(final Block blk) { + waiting.add(blk); + } + + /** + * Insert a block at the front of the queue of pending blocks. + * + * @param blk + * The block to put at the front. + */ + public void pushBlock(final Block blk) { + waiting.push(blk); + } + + @Override + public String toString() { + return String.format("PushbackBlockReader [waiting=%s, curBlock=%s, blockNo=%s]", waiting, curBlock, + blockNo); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java b/base/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java new file mode 100644 index 0000000..c229da1 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java @@ -0,0 +1,102 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; +import java.util.Deque; + +/** + * Provides a means of concatenating two block readers. + * + * @author bjculkin + * + */ +public class SerialBlockReader implements BlockReader { + private Deque readerQueue; + + private int blockNo; + + /** + * Create a new serial block reader. + * + * @param readers + * The readers to pull from, in the order to pull from + * them. + */ + public SerialBlockReader(final BlockReader... readers) { + for (final BlockReader reader : readers) { + readerQueue.add(reader); + } + } + + @Override + public boolean hasNextBlock() { + if (readerQueue.isEmpty()) return false; + + /* + * Attempt to get a block from the first reader. + */ + boolean hasBlock = readerQueue.peek().hasNextBlock(); + boolean cont = hasBlock || readerQueue.isEmpty(); + + /* + * Close/dispose of readers until we get an open one. + */ + while (!cont) { + try { + readerQueue.pop().close(); + } catch (final IOException ioex) { + throw new IllegalStateException("Exception thrown by discarded reader", ioex); + } + + hasBlock = readerQueue.peek().hasNextBlock(); + cont = hasBlock || readerQueue.isEmpty(); + } + + return hasBlock; + } + + @Override + public Block getBlock() { + if (readerQueue.isEmpty()) + return null; + else return readerQueue.peek().getBlock(); + } + + @Override + public boolean nextBlock() { + if (readerQueue.isEmpty()) return false; + + boolean gotBlock = readerQueue.peek().nextBlock(); + boolean cont = gotBlock || readerQueue.isEmpty(); + + while (!cont) { + try { + readerQueue.pop().close(); + } catch (final IOException ioex) { + throw new IllegalStateException("Exception thrown by discarded reader", ioex); + } + + gotBlock = readerQueue.peek().nextBlock(); + cont = gotBlock || readerQueue.isEmpty(); + } + + if (cont) { + blockNo += 1; + } + + return cont; + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + while (!readerQueue.isEmpty()) { + final BlockReader reader = readerQueue.pop(); + + reader.close(); + } + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java b/base/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java new file mode 100644 index 0000000..734bde8 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java @@ -0,0 +1,115 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.Reader; +import java.util.NoSuchElementException; +import java.util.Scanner; +import java.util.regex.Pattern; + +import bjc.utils.funcutils.StringUtils; +/** + * Simple implementation of {@link BlockReader} + * + * NOTE: The EOF marker is always treated as a delimiter. You are expected to + * handle blocks that may be shorter than you expect. + * + * @author EVE + * + */ +public class SimpleBlockReader implements BlockReader { + /* + * I/O source for blocks. + */ + private final Scanner blockReader; + + /* + * The current block. + */ + private Block currBlock; + + /* + * Info about the current block. + */ + private int blockNo; + private int lineNo; + + /** + * Create a new block reader. + * + * @param blockDelim + * The pattern that separates blocks. Note that the end + * of file is always considered to end a block. + * + * @param source + * The source to read blocks from. + */ + public SimpleBlockReader(final String blockDelim, final Reader source) { + blockReader = new Scanner(source); + + final String pattern = String.format("(?:%s)|\\Z", blockDelim); + final Pattern pt = Pattern.compile(pattern, Pattern.MULTILINE); + + blockReader.useDelimiter(pt); + + lineNo = 1; + } + + @Override + public boolean hasNextBlock() { + return blockReader.hasNext(); + } + + @Override + public Block getBlock() { + return currBlock; + } + + @Override + public boolean nextBlock() { + try { + /* + * Read in a new block, and keep the line numbers sane. + */ + final int blockStartLine = lineNo; + final String blockContents = blockReader.next(); + final int blockEndLine = lineNo + StringUtils.countMatches(blockContents, "\\R"); + + lineNo = blockEndLine; + blockNo += 1; + + currBlock = new Block(blockNo, blockContents, blockStartLine, blockEndLine); + + return true; + } catch (final NoSuchElementException nseex) { + currBlock = null; + + return false; + } + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + blockReader.close(); + } + + /** + * Set the delimiter used to separate blocks. + * + * @param delim + * The delimiter used to separate blocks. + */ + public void setDelimiter(final String delim) { + blockReader.useDelimiter(delim); + } + + @Override + public String toString() { + return String.format("SimpleBlockReader [currBlock=%s, blockNo=%s]", currBlock, blockNo); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/ToggledBlockReader.java b/base/src/main/java/bjc/utils/ioutils/blocks/ToggledBlockReader.java new file mode 100644 index 0000000..8f39b8f --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/ToggledBlockReader.java @@ -0,0 +1,63 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; + +import bjc.utils.data.BooleanToggle; + +public class ToggledBlockReader implements BlockReader { + private BlockReader leftSource; + private BlockReader rightSource; + + /* + * We choose the left source when this is true. + */ + private BooleanToggle leftToggle; + + private int blockNo; + + public ToggledBlockReader(BlockReader left, BlockReader right) { + leftSource = left; + rightSource = right; + + blockNo = 0; + + leftToggle = new BooleanToggle(); + } + + @Override + public boolean hasNextBlock() { + if(leftToggle.peek()) return leftSource.hasNextBlock(); + else return rightSource.hasNextBlock(); + } + + @Override + public Block getBlock() { + if(leftToggle.peek()) return leftSource.getBlock(); + else return rightSource.getBlock(); + } + + @Override + public boolean nextBlock() { + boolean succ; + + if(leftToggle.get()) { + succ = leftSource.nextBlock(); + } else { + succ = rightSource.nextBlock(); + } + + if(succ) blockNo += 1; + return succ; + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + leftSource.close(); + rightSource.close(); + } +} diff --git a/base/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java b/base/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java new file mode 100644 index 0000000..3a1e393 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java @@ -0,0 +1,70 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; + +/** + * A block reader that fires an action before a block is actually read. + * + * @author bjculkin + * + */ +public class TriggeredBlockReader implements BlockReader { + private final BlockReader source; + + private int blockNo; + + /* + * The action to fire. + */ + private final Runnable action; + + /** + * Create a new triggered reader with the specified source/action. + * + * @param source + * The block reader to read blocks from. + * + * @param action + * The action to execute before reading a block. + */ + public TriggeredBlockReader(final BlockReader source, final Runnable action) { + this.source = source; + this.action = action; + + blockNo = 0; + } + + @Override + public boolean hasNextBlock() { + action.run(); + + return source.hasNextBlock(); + } + + @Override + public Block getBlock() { + return source.getBlock(); + } + + @Override + public boolean nextBlock() { + blockNo += 1; + + return source.nextBlock(); + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + source.close(); + } + + @Override + public String toString() { + return String.format("TriggeredBlockReader [source=%s]", source); + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/DoubleMatcher.java b/base/src/main/java/bjc/utils/parserutils/DoubleMatcher.java new file mode 100644 index 0000000..a885808 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/DoubleMatcher.java @@ -0,0 +1,46 @@ +package bjc.utils.parserutils; + +import static bjc.utils.PropertyDB.applyFormat; +import static bjc.utils.PropertyDB.getRegex; + +import java.util.regex.Pattern; + +/* + * Checks if a string would pass Double.parseDouble. + * + * Uses a regex from the javadoc for Double.valueOf() + */ +class DoubleMatcher { + /* + * Unit pieces. + */ + private static final String rDecDigits = getRegex("fpDigits"); + private static final String rHexDigits = getRegex("fpHexDigits"); + private static final String rExponent = applyFormat("fpExponent", getRegex("fpExponent"), rDecDigits); + + /* + * Decimal floating point numbers. + */ + private static final String rSimpleDec = applyFormat("fpDecimalDecimal", rDecDigits, rExponent); + private static final String rSimpleIntDec = applyFormat("fpDecimalInteger", rDecDigits, rExponent); + + /* + * Hex floating point numbers. + */ + private static final String rHexInt = applyFormat("fpHexInteger", rHexDigits); + private static final String rHexDec = applyFormat("fpHexDecimal", rHexDigits); + private static final String rHexLead = applyFormat("fpHexLeader", rHexInt, rHexDec); + private static final String rHexString = applyFormat("fpHexString", rHexLead, rDecDigits); + + /* + * Floating point components. + */ + private static final String rFPLeader = getRegex("fpLeader"); + private static final String rFPNum = applyFormat("fpNumber", rSimpleIntDec, rSimpleDec, rHexString); + + /* + * Full double. + */ + private static final String rDouble = applyFormat("fpDouble", rFPLeader, rFPNum); + public static final Pattern doubleLiteral = Pattern.compile("\\A" + rDouble + "\\Z"); +} diff --git a/base/src/main/java/bjc/utils/parserutils/IPrecedent.java b/base/src/main/java/bjc/utils/parserutils/IPrecedent.java new file mode 100644 index 0000000..aa366cf --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/IPrecedent.java @@ -0,0 +1,28 @@ +package bjc.utils.parserutils; + +/** + * Represents something that has a set precedence + * + * @author ben + * + */ +@FunctionalInterface +public interface IPrecedent { + /** + * Create a new object with set precedence + * + * @param precedence + * The precedence of the object to handle + * @return A new object with set precedence + */ + public static IPrecedent newSimplePrecedent(final int precedence) { + return () -> precedence; + } + + /** + * Get the precedence of the attached object + * + * @return The precedence of the attached object + */ + public int getPrecedence(); +} diff --git a/base/src/main/java/bjc/utils/parserutils/ParserException.java b/base/src/main/java/bjc/utils/parserutils/ParserException.java new file mode 100644 index 0000000..ae33aba --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/ParserException.java @@ -0,0 +1,36 @@ +package bjc.utils.parserutils; + +/** + * General superclass for exceptions thrown during parsing. + * + * @author EVE + * + */ +public class ParserException extends Exception { + /** + * + */ + private static final long serialVersionUID = 631298568113373233L; + + /** + * Create a new exception with the provided message. + * + * @param msg + * The message for the exception. + */ + public ParserException(final String msg) { + super(msg); + } + + /** + * Create a new exception with the provided message and cause. + * + * @param msg + * The message for the exception. + * @param cause + * The cause of the exception. + */ + public ParserException(final String msg, final Exception cause) { + super(msg, cause); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/parserutils/ShuntingYard.java b/base/src/main/java/bjc/utils/parserutils/ShuntingYard.java new file mode 100644 index 0000000..a1b5feb --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/ShuntingYard.java @@ -0,0 +1,274 @@ +package bjc.utils.parserutils; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.function.Consumer; +import java.util.function.Function; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; +import bjc.utils.funcutils.StringUtils; + +/** + * Utility to run the shunting yard algorithm on a bunch of tokens. + * + * @author ben + * + * @param + * The type of tokens being shunted. + */ +public class ShuntingYard { + /** + * A enum representing the fundamental operator types. + * + * @author ben + * + */ + public static enum Operator implements IPrecedent { + /** + * Represents addition. + */ + ADD(1), + /** + * Represents subtraction. + */ + SUBTRACT(2), + + /** + * Represents multiplication. + */ + MULTIPLY(3), + /** + * Represents division. + */ + DIVIDE(4); + + private final int precedence; + + private Operator(final int prec) { + precedence = prec; + } + + @Override + public int getPrecedence() { + return precedence; + } + } + + /* + * Function that shunts tokens. + */ + private final class TokenShunter implements Consumer { + private final IList output; + private final Deque stack; + private final Function transformer; + + public TokenShunter(final IList outpt, final Deque stack, + final Function transformer) { + this.output = outpt; + this.stack = stack; + this.transformer = transformer; + } + + @Override + public void accept(final String token) { + /* + * Handle operators + */ + if (operators.containsKey(token)) { + /* + * Pop operators while there isn't a higher precedence one + */ + while (!stack.isEmpty() && isHigherPrec(token, stack.peek())) { + output.add(transformer.apply(stack.pop())); + } + + /* + * Put this operator onto the stack + */ + 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 + */ + final String swappedToken = token.replace(')', '('); + + /* + * Remove tokens up to a matching parenthesis + */ + while (!stack.peek().equals(swappedToken)) { + output.add(transformer.apply(stack.pop())); + } + + /* + * Remove the parenthesis + */ + stack.pop(); + } else { + /* + * Just add the transformed token + */ + output.add(transformer.apply(token)); + } + } + } + + /* + * Holds all the shuntable operations. + */ + private IMap operators; + + /** + * Create a new shunting yard with a default set of operators. + * + * @param configureBasics + * Whether or not basic math operators should be + * provided. + */ + public ShuntingYard(final boolean configureBasics) { + operators = new FunctionalMap<>(); + + /* + * Add basic operators if we're configured to do so + */ + if (configureBasics) { + operators.put("+", Operator.ADD); + operators.put("-", Operator.SUBTRACT); + operators.put("*", Operator.MULTIPLY); + operators.put("/", Operator.DIVIDE); + } + } + + /** + * Add an operator to the list of shuntable operators. + * + * @param operator + * The token representing the operator. + * + * @param precedence + * The precedence of the operator to add. + */ + public void addOp(final String operator, final int precedence) { + /* + * Create the precedence marker + */ + final IPrecedent prec = IPrecedent.newSimplePrecedent(precedence); + + this.addOp(operator, prec); + } + + /** + * Add an operator to the list of shuntable operators. + * + * @param operator + * The token representing the operator. + * + * @param precedence + * The precedence of the operator. + */ + public void addOp(final String operator, final IPrecedent precedence) { + /* + * Complain about trying to add an incorrect operator + */ + if (operator == null) + throw new NullPointerException("Operator must not be null"); + else if (precedence == null) throw new NullPointerException("Precedence must not be null"); + + /* + * Add the operator to the ones we handle + */ + operators.put(operator, precedence); + } + + private boolean isHigherPrec(final String left, final String right) { + /* + * Check if the right operator exists + */ + final boolean exists = operators.containsKey(right); + + /* + * If it doesn't, the left is higher precedence. + */ + if (!exists) return false; + + /* + * Get the precedence of operators + */ + final int rightPrecedence = operators.get(right).getPrecedence(); + final int leftPrecedence = operators.get(left).getPrecedence(); + + /* + * Evaluate what we were asked + */ + return rightPrecedence >= leftPrecedence; + } + + /** + * Transform a string of tokens from infix notation to postfix. + * + * @param input + * The string to transform. + * + * @param transformer + * The function to use to transform strings to tokens. + * + * @return A list of tokens in postfix notation. + */ + public IList postfix(final IList input, final Function transformer) { + /* + * Check our input + */ + if (input == null) + throw new NullPointerException("Input must not be null"); + else if (transformer == null) throw new NullPointerException("Transformer must not be null"); + + /* + * Here's what we're handing back + */ + final IList output = new FunctionalList<>(); + + /* + * The stack to put operators on + */ + final Deque stack = new LinkedList<>(); + + /* + * Shunt the tokens + */ + input.forEach(new TokenShunter(output, stack, transformer)); + + /* + * Transform any resulting tokens + */ + stack.forEach(token -> { + output.add(transformer.apply(token)); + }); + + return output; + } + + /** + * Remove an operator from the list of shuntable operators. + * + * @param operator + * The token representing the operator. If null, remove + * all operators. + */ + public void removeOp(final String operator) { + /* + * Check if we want to remove all operators + */ + if (operator == null) { + operators = new FunctionalMap<>(); + } else { + operators.remove(operator); + } + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/StringDescaper.java b/base/src/main/java/bjc/utils/parserutils/StringDescaper.java new file mode 100644 index 0000000..096656a --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/StringDescaper.java @@ -0,0 +1,242 @@ +package bjc.utils.parserutils; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.UnaryOperator; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import static java.util.Map.Entry; + +import static bjc.utils.PropertyDB.applyFormat; +import static bjc.utils.PropertyDB.getCompiledRegex; +import static bjc.utils.PropertyDB.getRegex; + +public class StringDescaper { + private Logger LOGGER = Logger.getLogger(StringDescaper.class.getName()); + + /* + * Patterns and pattern parts. + */ + private static String rPossibleEscapeString = getRegex("possibleStringEscape"); + private static Pattern possibleEscapePatt = Pattern.compile(rPossibleEscapeString); + + private static String rShortEscape = getRegex("shortFormStringEscape"); + private static String rOctalEscape = getRegex("octalStringEscape"); + private static String rUnicodeEscape = getRegex("unicodeStringEscape"); + + private String rEscapeString; + private Pattern escapePatt; + + private static String rDoubleQuoteString = applyFormat("doubleQuotes", getRegex("nonStringEscape"), rPossibleEscapeString); + private static Pattern doubleQuotePatt = Pattern.compile(rDoubleQuoteString); + + private static Pattern quotePatt = getCompiledRegex("unescapedQuote"); + + private Map literalEscapes; + private Map> specialEscapes; + + public StringDescaper() { + literalEscapes = new HashMap<>(); + specialEscapes = new HashMap<>(); + + rEscapeString = String.format("\\\\(%1$s|%2$s|%3$s)"); + escapePatt = Pattern.compile(rEscapeString); + } + + public void addLiteralEscape(String escape, String val) { + if(literalEscapes.containsKey(escape)) { + LOGGER.warning(String.format("Shadowing literal escape '%s'\n", escape)); + } + + literalEscapes.put(escape, val); + } + + public void addSpecialEscape(String escape, UnaryOperator val) { + if(specialEscapes.containsKey(escape)) { + LOGGER.warning(String.format("Shadowing special escape '%s'\n", escape)); + } + + /* + * Make sure this special escape is a valid regex. + */ + + Pattern patt = null; + try { + patt = Pattern.compile(escape); + } catch (PatternSyntaxException psex) { + String msg = String.format("Invalid special escape '%s'", escape); + + IllegalArgumentException iaex = new IllegalArgumentException(msg); + iaex.initCause(psex); + + throw psex; + } + + specialEscapes.put(patt, val); + } + + public void compileEscapes() { + StringBuilder work = new StringBuilder(); + + for(String litEscape : literalEscapes.keySet()) { + work.append("|(?:"); + work.append(Pattern.quote(litEscape)); + work.append(")"); + } + + for(Pattern specEscape : specialEscapes.keySet()) { + work.append("|(?:"); + work.append(specEscape.toString()); + work.append(")"); + } + + /* + * Convert user-defined escapes to a regex for matching. + * We don't need a bar before %4 because the string has it. + */ + rEscapeString = String.format("\\(%1$s|%2$s|%3$s%4$s)", rShortEscape, rOctalEscape, rUnicodeEscape, work.toString()); + escapePatt = Pattern.compile(rEscapeString); + } + + /** + * Replace escape characters with their actual equivalents. + * + * @param inp + * The string to replace escape sequences in. + * + * @return The string with escape sequences replaced by their equivalent + * characters. + */ + public String descapeString(final String inp) { + if (inp == null) { + throw new NullPointerException("Input to descapeString must not be null"); + } + + /* + * Prepare the buffer and escape finder. + */ + final StringBuffer work = new StringBuffer(); + final Matcher possibleEscapeFinder = possibleEscapePatt.matcher(inp); + final Matcher escapeFinder = escapePatt.matcher(inp); + + while (possibleEscapeFinder.find()) { + if (!escapeFinder.find()) { + /* + * Found a possible escape that isn't actually an + * escape. + */ + final String msg = String.format("Illegal escape sequence '%s' at position %d of string '%s'", + possibleEscapeFinder.group(), possibleEscapeFinder.start(), inp); + throw new IllegalArgumentException(msg); + } + + final String escapeSeq = escapeFinder.group(); + + /* + * Convert the escape to a string. + */ + String escapeRep = ""; + switch (escapeSeq) { + case "\\b": + escapeRep = "\b"; + break; + case "\\t": + escapeRep = "\t"; + break; + case "\\n": + escapeRep = "\n"; + break; + case "\\f": + escapeRep = "\f"; + break; + case "\\r": + escapeRep = "\r"; + break; + case "\\\"": + escapeRep = "\""; + break; + case "\\'": + escapeRep = "'"; + break; + case "\\\\": + /* + * Skip past the second slash. + */ + possibleEscapeFinder.find(); + escapeRep = "\\"; + break; + default: + if (escapeSeq.startsWith("u")) { + escapeRep = handleUnicodeEscape(escapeSeq.substring(1)); + } else if(escapeSeq.startsWith("O")) { + escapeRep = handleOctalEscape(escapeSeq.substring(1)); + } else if(literalEscapes.containsKey(escapeSeq)) { + escapeRep = literalEscapes.get(escapeSeq); + } else { + for(Entry> ent : specialEscapes.entrySet()) { + Pattern pat = ent.getKey(); + + Matcher mat = pat.matcher(escapeSeq); + if(mat.matches()) { + escapeRep = ent.getValue().apply(escapeSeq); + break; + } + } + } + } + + escapeFinder.appendReplacement(work, escapeRep); + } + + escapeFinder.appendTail(work); + + return work.toString(); + } + + /* + * Handle a unicode codepoint. + */ + private static String handleUnicodeEscape(final String seq) { + try { + final int codepoint = Integer.parseInt(seq, 16); + + return new String(Character.toChars(codepoint)); + } catch (final IllegalArgumentException iaex) { + final String msg = String.format("'%s' is not a valid Unicode escape sequence'", seq); + + final IllegalArgumentException reiaex = new IllegalArgumentException(msg); + + reiaex.initCause(iaex); + + throw reiaex; + } + } + + /* + * Handle a octal codepoint. + */ + private static String handleOctalEscape(final String seq) { + try { + final int codepoint = Integer.parseInt(seq, 8); + + if (codepoint > 255) { + final String msg = String.format("'%d' is outside the range of octal escapes', codepoint"); + + throw new IllegalArgumentException(msg); + } + + return new String(Character.toChars(codepoint)); + } catch (final IllegalArgumentException iaex) { + final String msg = String.format("'%s' is not a valid octal escape sequence'", seq); + + final IllegalArgumentException reiaex = new IllegalArgumentException(msg); + + reiaex.initCause(iaex); + + throw reiaex; + } + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/TokenTransformer.java b/base/src/main/java/bjc/utils/parserutils/TokenTransformer.java new file mode 100644 index 0000000..30ccc5a --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/TokenTransformer.java @@ -0,0 +1,131 @@ +package bjc.utils.parserutils; + +import java.util.Deque; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +import bjc.utils.data.IHolder; +import bjc.utils.data.ITree; +import bjc.utils.data.Pair; +import bjc.utils.data.Tree; +import bjc.utils.parserutils.TreeConstructor.ConstructorState; +import bjc.utils.parserutils.TreeConstructor.QueueFlattener; + +/* + * Handle creating ASTs from tokens. + */ +final class TokenTransformer implements Consumer { + /* + * Handle operators + */ + private final class OperatorHandler implements UnaryOperator> { + private final TokenType element; + + public OperatorHandler(final TokenType element) { + this.element = element; + } + + @Override + public ConstructorState apply(final ConstructorState pair) { + /* + * Replace the current AST with the result of handling an operator + */ + return new ConstructorState<>(pair.bindLeft(queuedASTs -> { + return handleOperator(queuedASTs); + })); + } + + private ConstructorState handleOperator(final Deque> queuedASTs) { + /* + * The AST we're going to hand back + */ + ITree newAST; + + /* + * Handle special operators + */ + if (isSpecialOperator.test(element)) { + newAST = handleSpecialOperator.apply(element).apply(queuedASTs); + } else { + /* + * Error if we don't have enough for a binary operator + */ + if (queuedASTs.size() < 2) { + final String msg = String.format( + "Attempted to parse binary operator without enough operands\n\tProblem operator is: %s\n\tPossible operand is: %s", + element.toString(), queuedASTs.peek().toString()); + + throw new IllegalStateException(msg); + } + + /* + * Grab the two operands + */ + final ITree right = queuedASTs.pop(); + final ITree left = queuedASTs.pop(); + + /* + * Create a new AST + */ + newAST = new Tree<>(element, left, right); + } + + /* + * Stick it onto the stack + */ + queuedASTs.push(newAST); + + /* + * Hand back the state + */ + return new ConstructorState<>(queuedASTs, newAST); + } + } + + private final IHolder> initialState; + + private final Predicate operatorPredicate; + + private final Predicate isSpecialOperator; + private final Function> handleSpecialOperator; + + /* + * Create a new transformer + */ + public TokenTransformer(final IHolder> initialState, + final Predicate operatorPredicate, final Predicate isSpecialOperator, + final Function> handleSpecialOperator) { + this.initialState = initialState; + this.operatorPredicate = operatorPredicate; + this.isSpecialOperator = isSpecialOperator; + this.handleSpecialOperator = handleSpecialOperator; + } + + @Override + public void accept(final TokenType element) { + /* + * Handle operators + */ + if (operatorPredicate.test(element)) { + initialState.transform(new OperatorHandler(element)); + } else { + final ITree newAST = new Tree<>(element); + + /* + * Insert the new tree into the AST + */ + initialState.transform(pair -> { + /* + * Transform the pair, ignoring the current AST in favor of the one consisting of the current element + */ + return new ConstructorState<>(pair.bindLeft(queue -> { + queue.push(newAST); + + return new Pair<>(queue, newAST); + })); + }); + } + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/TokenUtils.java b/base/src/main/java/bjc/utils/parserutils/TokenUtils.java new file mode 100644 index 0000000..67c1e5a --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/TokenUtils.java @@ -0,0 +1,303 @@ +package bjc.utils.parserutils; + +import static bjc.utils.PropertyDB.applyFormat; +import static bjc.utils.PropertyDB.getCompiledRegex; +import static bjc.utils.PropertyDB.getRegex; + +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; +import bjc.utils.parserutils.splitter.TokenSplitter; + +/** + * Utilities useful for operating on PL tokens. + * + * @author EVE + * + */ +public class TokenUtils { + /** + * Simple implementation of TokenSplitter for removing double-quoted + * strings. + * + * @author EVE + * + */ + public static class StringTokenSplitter implements TokenSplitter { + @Override + public IList split(final String input) { + return new FunctionalList<>(TokenUtils.removeDQuotedStrings(input)); + } + } + + /* + * Patterns and pattern parts. + */ + private static String rPossibleEscapeString = getRegex("possibleStringEscape"); + + private static Pattern possibleEscapePatt = Pattern.compile(rPossibleEscapeString); + + private static String rShortEscape = getRegex("shortFormStringEscape"); + private static String rOctalEscape = getRegex("octalStringEscape"); + private static String rUnicodeEscape = getRegex("unicodeStringEscape"); + + private static String rEscapeString = applyFormat("stringEscape", rShortEscape, rOctalEscape, rUnicodeEscape); + + private static Pattern escapePatt = Pattern.compile(rEscapeString); + + private static String rDoubleQuoteString = applyFormat("doubleQuotes", getRegex("nonStringEscape"), + rPossibleEscapeString); + + private static Pattern doubleQuotePatt = Pattern.compile(rDoubleQuoteString); + + private static Pattern quotePatt = getCompiledRegex("unescapedQuote"); + + private static Pattern intLitPattern = getCompiledRegex("intLiteral"); + + /** + * Remove double quoted strings from a string. + * + * Splits a string around instances of java-style double-quoted strings. + * + * @param inp + * The string to split. + * + * @return An list containing alternating bits of the string and the + * embedded double-quoted strings that separated them. + */ + public static List removeDQuotedStrings(final String inp) { + if (inp == null) throw new NullPointerException("inp must not be null"); + + /* + * What we need for piece-by-piece string building + */ + StringBuffer work = new StringBuffer(); + final List res = new LinkedList<>(); + + /* + * Matcher for proper strings and single quotes. + */ + final Matcher mt = doubleQuotePatt.matcher(inp); + final Matcher corr = quotePatt.matcher(inp); + + if (corr.find() && !corr.find()) { + /* + * There's a unmatched opening quote with no strings. + */ + final String msg = String.format( + "Unclosed string literal '%s'. Opening quote was at position %d", inp, + inp.indexOf("\"")); + + throw new IllegalArgumentException(msg); + } + + while (mt.find()) { + /* + * Remove the string until the quoted string. + */ + mt.appendReplacement(work, ""); + + /* + * Add the string preceding the double-quoted string and + * the double-quoted string to the list. + */ + res.add(work.toString()); + res.add(mt.group(1)); + + /* + * Renew the buffer. + */ + work = new StringBuffer(); + } + + /* + * Grab the remainder of the string. + */ + mt.appendTail(work); + final String tail = work.toString(); + + if (tail.contains("\"")) { + /* + * There's a unmatched opening quote with at least one + * string. + */ + final String msg = String.format( + "Unclosed string literal '%s'. Opening quote was at position %d", inp, + inp.lastIndexOf("\"")); + + throw new IllegalArgumentException(msg); + } + + /* + * Only add an empty tail if the string was empty. + */ + if (!tail.equals("") || res.isEmpty()) { + res.add(tail); + } + + return res; + } + + /** + * Replace escape characters with their actual equivalents. + * + * @param inp + * The string to replace escape sequences in. + * + * @return The string with escape sequences replaced by their equivalent + * characters. + */ + public static String descapeString(final String inp) { + if (inp == null) throw new NullPointerException("inp must not be null"); + + /* + * Prepare the buffer and escape finder. + */ + final StringBuffer work = new StringBuffer(); + final Matcher possibleEscapeFinder = possibleEscapePatt.matcher(inp); + final Matcher escapeFinder = escapePatt.matcher(inp); + + while (possibleEscapeFinder.find()) { + if (!escapeFinder.find()) { + /* + * Found a possible escape that isn't actually an + * escape. + */ + final String msg = String.format("Illegal escape sequence '%s' at position %d", + possibleEscapeFinder.group(), possibleEscapeFinder.start()); + + throw new IllegalArgumentException(msg); + } + + final String escapeSeq = escapeFinder.group(); + + /* + * Convert the escape to a string. + */ + String escapeRep = ""; + switch (escapeSeq) { + case "\\b": + escapeRep = "\b"; + break; + case "\\t": + escapeRep = "\t"; + break; + case "\\n": + escapeRep = "\n"; + break; + case "\\f": + escapeRep = "\f"; + break; + case "\\r": + escapeRep = "\r"; + break; + case "\\\"": + escapeRep = "\""; + break; + case "\\'": + escapeRep = "'"; + break; + case "\\\\": + /* + * Skip past the second slash. + */ + possibleEscapeFinder.find(); + escapeRep = "\\"; + break; + default: + if (escapeSeq.startsWith("u")) { + escapeRep = handleUnicodeEscape(escapeSeq.substring(1)); + } else { + escapeRep = handleOctalEscape(escapeSeq); + } + } + + escapeFinder.appendReplacement(work, escapeRep); + } + + escapeFinder.appendTail(work); + + return work.toString(); + } + + /* + * Handle a unicode codepoint. + */ + private static String handleUnicodeEscape(final String seq) { + try { + final int codepoint = Integer.parseInt(seq, 16); + + return new String(Character.toChars(codepoint)); + } catch (final IllegalArgumentException iaex) { + final String msg = String.format("'%s' is not a valid Unicode escape sequence'", seq); + + final IllegalArgumentException reiaex = new IllegalArgumentException(msg); + + reiaex.initCause(iaex); + + throw reiaex; + } + } + + /* + * Handle a octal codepoint. + */ + private static String handleOctalEscape(final String seq) { + try { + final int codepoint = Integer.parseInt(seq, 8); + + if (codepoint > 255) { + final String msg = String + .format("'%d' is outside the range of octal escapes', codepoint"); + + throw new IllegalArgumentException(msg); + } + + return new String(Character.toChars(codepoint)); + } catch (final IllegalArgumentException iaex) { + final String msg = String.format("'%s' is not a valid octal escape sequence'", seq); + + final IllegalArgumentException reiaex = new IllegalArgumentException(msg); + + reiaex.initCause(iaex); + + throw reiaex; + } + } + + /** + * Check if a given string would be successfully converted to a double + * by {@link Double#parseDouble(String)}. + * + * @param inp + * The string to check. + * @return Whether the string is a valid double or not. + */ + public static boolean isDouble(final String inp) { + return DoubleMatcher.doubleLiteral.matcher(inp).matches(); + } + + /** + * Check if a given string would be successfully converted to a integer + * by {@link Integer#parseInt(String)}. + * + * NOTE: This only checks syntax. Using values out of the range of + * integers will still cause errors. + * + * @param inp + * The input to check. + * @return Whether the string is a valid integer or not. + */ + public static boolean isInt(final String inp) { + try { + Integer.parseInt(inp); + return true; + } catch (NumberFormatException nfex) { + return false; + } + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/TreeConstructor.java b/base/src/main/java/bjc/utils/parserutils/TreeConstructor.java new file mode 100644 index 0000000..90141ef --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/TreeConstructor.java @@ -0,0 +1,125 @@ +package bjc.utils.parserutils; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.function.Function; +import java.util.function.Predicate; + +import bjc.utils.data.IHolder; +import bjc.utils.data.IPair; +import bjc.utils.data.ITree; +import bjc.utils.data.Identity; +import bjc.utils.data.Pair; +import bjc.utils.funcdata.IList; + +/** + * Creates a parse tree from a postfix expression + * + * @author ben + * + */ +public class TreeConstructor { + /** + * Alias interface for special operator types. + * + * @param + * The token type of the tree. + */ + public interface QueueFlattener extends Function>, ITree> { + + } + + /* + * Alias for constructor state. + */ + static final class ConstructorState extends Pair>, ITree> { + public ConstructorState(final Deque> left, final ITree right) { + super(left, right); + } + + public ConstructorState(final IPair>, ITree> par) { + super(par.getLeft(), par.getRight()); + } + } + + /** + * Construct a tree from a list of tokens in postfix notation + * + * Only binary operators are accepted. + * + * @param + * The elements of the parse tree + * @param tokens + * The list of tokens to build a tree from + * @param isOperator + * The predicate to use to determine if something is a + * operator + * @return A AST from the expression + */ + public static ITree constructTree(final IList tokens, + final Predicate isOperator) { + /* + * Construct a tree with no special operators + */ + return constructTree(tokens, isOperator, op -> false, null); + } + + /** + * Construct a tree from a list of tokens in postfix notation. + * + * Only binary operators are accepted by default. Use the last two + * parameters to handle non-binary operators. + * + * @param + * The elements of the parse tree. + * + * @param tokens + * The list of tokens to build a tree from. + * + * @param isOperator + * The predicate to use to determine if something is a + * operator. + * + * @param isSpecialOperator + * The predicate to use to determine if an operator needs + * special handling. + * + * @param handleSpecialOperator + * The function to use to handle special case operators. + * + * @return A AST from the expression + * + */ + public static ITree constructTree(final IList tokens, + final Predicate isOperator, final Predicate isSpecialOperator, + final Function> handleSpecialOperator) { + /* + * Make sure our parameters are valid + */ + if (tokens == null) + throw new NullPointerException("Tokens must not be null"); + else if (isOperator == null) + throw new NullPointerException("Operator predicate must not be null"); + else if (isSpecialOperator == null) + throw new NullPointerException("Special operator determiner must not be null"); + + /* + * Here is the state for the tree construction + */ + final IHolder> initialState = new Identity<>( + new ConstructorState<>(new LinkedList<>(), null)); + + /* + * Transform each of the tokens + */ + tokens.forEach(new TokenTransformer<>(initialState, isOperator, isSpecialOperator, + handleSpecialOperator)); + + /* + * Grab the tree from the state + */ + return initialState.unwrap(pair -> { + return pair.getRight(); + }); + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/defines/IteratedDefine.java b/base/src/main/java/bjc/utils/parserutils/defines/IteratedDefine.java new file mode 100644 index 0000000..552b471 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/defines/IteratedDefine.java @@ -0,0 +1,48 @@ +package bjc.utils.parserutils.defines; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.function.UnaryOperator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import bjc.utils.data.CircularIterator; + +public class IteratedDefine implements UnaryOperator { + private Pattern patt; + + private Iterator repls; + + /** + * Create a new iterated define. + * + * @param pattern + * The pattern to use for matching. + * @param circular + * Whether or not to loop through the list of replacers, or just + * repeat the last one. + * @param replacers + * The set of replacers to use. + */ + public IteratedDefine(Pattern pattern, boolean circular, String... replacers) { + patt = pattern; + + repls = new CircularIterator<>(Arrays.asList(replacers), circular); + } + + @Override + public String apply(String ln) { + Matcher mat = patt.matcher(ln); + StringBuffer sb = new StringBuffer(); + + while(mat.find()) { + String repl = repls.next(); + + mat.appendReplacement(sb, repl); + } + + mat.appendTail(sb); + + return sb.toString(); + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/defines/SimpleDefine.java b/base/src/main/java/bjc/utils/parserutils/defines/SimpleDefine.java new file mode 100644 index 0000000..42866c2 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/defines/SimpleDefine.java @@ -0,0 +1,23 @@ +package bjc.utils.parserutils.defines; + +import java.util.function.UnaryOperator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SimpleDefine implements UnaryOperator { + private Pattern patt; + private String repl; + + public SimpleDefine(Pattern pattern, String replace) { + patt = pattern; + + repl = replace; + } + + @Override + public String apply(String line) { + Matcher mat = patt.matcher(line); + + return mat.replaceAll(repl); + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/delims/DelimiterException.java b/base/src/main/java/bjc/utils/parserutils/delims/DelimiterException.java new file mode 100644 index 0000000..071afb4 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/delims/DelimiterException.java @@ -0,0 +1,21 @@ +package bjc.utils.parserutils.delims; + +/** + * The superclass for exceptions thrown during sequence delimitation. + */ +public class DelimiterException extends RuntimeException { + /** + * + */ + private static final long serialVersionUID = 2079514406049040888L; + + /** + * Create a new generic delimiter exception. + * + * @param res + * The reason for this exception. + */ + public DelimiterException(final String res) { + super(res); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java b/base/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java new file mode 100644 index 0000000..b1d8597 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/delims/DelimiterGroup.java @@ -0,0 +1,593 @@ +package bjc.utils.parserutils.delims; + +import java.util.Arrays; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.BiPredicate; +import java.util.function.Function; + +import bjc.utils.data.IPair; +import bjc.utils.data.ITree; +import bjc.utils.data.Pair; +import bjc.utils.data.Tree; +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * Represents a possible delimiter group to match. + * + * @author EVE + * + * @param + * The type of items in the sequence. + */ +public class DelimiterGroup { + /** + * Represents an instance of a delimiter group. + * + * @author EVE + * + */ + public class OpenGroup { + /* + * The contents of this group. + */ + private final Deque> contents; + + /* + * The contents of the current subgroup. + */ + private IList> currentGroup; + + /* + * The token that opened the group, and any opening parameters. + */ + private final T opener; + private final T[] params; + + /** + * Create a new instance of a delimiter group. + * + * @param open + * The item that opened this group. + * + * @param parms + * Any parameters from the opener. + */ + public OpenGroup(final T open, final T[] parms) { + opener = open; + params = parms; + + contents = new LinkedList<>(); + + currentGroup = new FunctionalList<>(); + } + + /** + * Add an item to this group instance. + * + * @param itm + * The item to add to this group instance. + */ + public void addItem(final ITree itm) { + currentGroup.add(itm); + } + + /** + * Mark a subgroup. + * + * @param marker + * The item that indicated this subgroup. + * + * @param chars + * The characteristics for building the tree. + */ + public void markSubgroup(final T marker, final SequenceCharacteristics chars) { + /* + * Add all of the contents to the subgroup. + */ + final ITree subgroupContents = new Tree<>(chars.contents); + for (final ITree itm : currentGroup) { + subgroupContents.addChild(itm); + } + + /* + * Handle subordinate sub-groups. + */ + while (!contents.isEmpty()) { + final ITree possibleSubordinate = contents.peek(); + + /* + * Subordinate lower priority subgroups. + */ + if (possibleSubordinate.getHead().equals(chars.subgroup)) { + final T otherMarker = possibleSubordinate.getChild(1).getHead(); + + if (subgroups.get(marker) > subgroups.get(otherMarker)) { + subgroupContents.prependChild(contents.pop()); + } else { + break; + } + } else { + subgroupContents.prependChild(contents.pop()); + } + } + + final Tree subgroup = new Tree<>(chars.subgroup, subgroupContents, new Tree<>(marker)); + + contents.push(subgroup); + + currentGroup = new FunctionalList<>(); + } + + /** + * Convert this group into a tree. + * + * @param closer + * The item that closed this group. + * + * @param chars + * The characteristics for building the tree. + * + * @return This group as a tree. + */ + public ITree toTree(final T closer, final SequenceCharacteristics chars) { + /* + * Mark any implied subgroups. + */ + if (impliedSubgroups.containsKey(closer)) { + markSubgroup(impliedSubgroups.get(closer), chars); + } + + final ITree res = new Tree<>(chars.contents); + + /* + * Add either the contents of the current group, + * or subgroups if they're their. + */ + if (contents.isEmpty()) { + currentGroup.forEach(res::addChild); + } else { + while (!contents.isEmpty()) { + res.prependChild(contents.poll()); + } + + currentGroup.forEach(res::addChild); + } + + return new Tree<>(groupName, new Tree<>(opener), res, new Tree<>(closer)); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + + builder.append("OpenGroup [contents="); + builder.append(contents); + builder.append(", currentGroup="); + builder.append(currentGroup); + builder.append(", opener="); + builder.append(opener); + builder.append("]"); + + return builder.toString(); + } + + /** + * Check if a group is excluded at the top level of this group. + * + * @param groupName + * The group to check. + * + * @return Whether or not the provided group is excluded. + */ + public boolean excludes(final T groupName) { + return topLevelExclusions.contains(groupName); + } + + /** + * Check if the provided delimiter would close this group. + * + * @param del + * The string to check as a closing delimiter. + * + * @return Whether or not the provided delimiter closes this + * group. + */ + public boolean isClosing(final T del) { + if (closingDelimiters.contains(del)) return true; + + for (final BiPredicate pred : predClosers) { + if (pred.test(del, params)) return true; + } + + return closingDelimiters.contains(del); + } + + /** + * Get the name of the group this is an instance of. + * + * @return The name of the group this is an instance of. + */ + public T getName() { + return groupName; + } + + /** + * Get the groups that aren't allowed at all in this group. + * + * @return The groups that aren't allowed at all in this group. + */ + public Set getNestingExclusions() { + return groupExclusions; + } + + /** + * Get the groups that are allowed to open anywhere inside this + * group. + * + * @return The groups allowed to open anywhere inside this + * group. + */ + public Map getNestingOpeners() { + return nestedOpenDelimiters; + } + + /** + * Checks if a given token marks a subgroup. + * + * @param tok + * The token to check. + * + * @return Whether or not the token marks a subgroup. + */ + public boolean marksSubgroup(final T tok) { + return subgroups.containsKey(tok); + } + + /** + * Checks if a given token opens a group. + * + * @param marker + * The token to check. + * + * @return The name of the group T opens, or null if it doesn't + * open one. + */ + public IPair doesOpen(final T marker) { + if (openDelimiters.containsKey(marker)) return new Pair<>(openDelimiters.get(marker), null); + + for (final Function> pred : predOpeners) { + final IPair par = pred.apply(marker); + + if (par.getLeft() != null) return par; + } + + return new Pair<>(null, null); + } + + /** + * Check if this group starts a new nesting scope. + * + * @return Whether this group starts a new nesting scope. + */ + public boolean isForgetful() { + return forgetful; + } + } + + /** + * The name of this delimiter group. + */ + public final T groupName; + + /* + * The delimiters that open groups at the top level of this group. + */ + private final Map openDelimiters; + + /* + * The delimiters that open groups inside of this group. + */ + private final Map nestedOpenDelimiters; + + /* + * The delimiters that close this group. + */ + private final Set closingDelimiters; + + /* + * The groups that can't occur in the top level of this group. + */ + private final Set topLevelExclusions; + + /* + * The groups that can't occur anywhere inside this group. + */ + private final Set groupExclusions; + + /* + * Mapping from sub-group delimiters, to any sub-groups enclosed in + * them. + */ + private final Map subgroups; + + /* + * Subgroups implied by a particular closing delimiter + */ + private final Map impliedSubgroups; + + /* + * Allows more complex openings + */ + private final List>> predOpeners; + + /* + * Allow more complex closings + */ + private final List> predClosers; + + /* + * Whether or not this group starts a new nesting set. + */ + private boolean forgetful; + + /** + * Create a new empty delimiter group. + * + * @param name + * The name of the delimiter group + */ + public DelimiterGroup(final T name) { + if (name == null) throw new NullPointerException("Group name must not be null"); + + groupName = name; + + openDelimiters = new HashMap<>(); + nestedOpenDelimiters = new HashMap<>(); + + closingDelimiters = new HashSet<>(); + + topLevelExclusions = new HashSet<>(); + groupExclusions = new HashSet<>(); + + subgroups = new HashMap<>(); + impliedSubgroups = new HashMap<>(); + + predOpeners = new LinkedList<>(); + predClosers = new LinkedList<>(); + } + + /** + * Adds one or more delimiters that close this group. + * + * @param closers + * Delimiters that close this group. + */ + @SafeVarargs + public final void addClosing(final T... closers) { + final List closerList = Arrays.asList(closers); + + for (final T closer : closerList) { + if (closer == null) + throw new NullPointerException("Closing delimiter must not be null"); + else if (closer.equals("")) + /* + * We can do this because equals works on + * arbitrary objects, not just those of the same + * type. + */ + throw new IllegalArgumentException("Empty string is not a valid exclusion"); + else { + closingDelimiters.add(closer); + } + } + } + + /** + * Adds one or more groups that cannot occur in the top level of this + * group. + * + * @param exclusions + * The groups forbidden in the top level of this group. + */ + @SafeVarargs + public final void addTopLevelForbid(final T... exclusions) { + for (final T exclusion : exclusions) { + if (exclusion == null) + throw new NullPointerException("Exclusion must not be null"); + else if (exclusion.equals("")) + /* + * We can do this because equals works on + * arbitrary objects, not just those of the same + * type. + */ + throw new IllegalArgumentException("Empty string is not a valid exclusion"); + else { + topLevelExclusions.add(exclusion); + } + } + } + + /** + * Adds one or more groups that cannot occur at all in this group. + * + * @param exclusions + * The groups forbidden inside this group. + */ + @SafeVarargs + public final void addGroupForbid(final T... exclusions) { + for (final T exclusion : exclusions) { + if (exclusion == null) + throw new NullPointerException("Exclusion must not be null"); + else if (exclusion.equals("")) + /* + * We can do this because equals works on + * arbitrary objects, not just those of the same + * type. + */ + throw new IllegalArgumentException("Empty string is not a valid exclusion"); + else { + groupExclusions.add(exclusion); + } + } + } + + /** + * Adds sub-group markers to this group. + * + * @param subgroup + * The token to mark a sub-group. + * + * @param priority + * The priority of this sub-group. + */ + public void addSubgroup(final T subgroup, final int priority) { + if (subgroup == null) throw new NullPointerException("Subgroup marker must not be null"); + + subgroups.put(subgroup, priority); + } + + /** + * Adds a marker that opens a group at the top level of this group. + * + * @param opener + * The marker that opens the group. + * + * @param group + * The group opened by the marker. + */ + public void addOpener(final T opener, final T group) { + if (opener == null) throw new NullPointerException("Opener must not be null"); + else if (group == null) throw new NullPointerException("Group to open must not be null"); + + openDelimiters.put(opener, group); + } + + /** + * Adds a marker that opens a group inside of this group. + * + * @param opener + * The marker that opens the group. + * + * @param group + * The group opened by the marker. + */ + public void addNestedOpener(final T opener, final T group) { + if (opener == null) throw new NullPointerException("Opener must not be null"); + else if (group == null) throw new NullPointerException("Group to open must not be null"); + + nestedOpenDelimiters.put(opener, group); + } + + /** + * Mark a closing delimiter as implying a subgroup. + * + * @param closer + * The closing delimiter. + * + * @param subgroup + * The subgroup to imply. + */ + public void implySubgroup(final T closer, final T subgroup) { + if (closer == null) throw new NullPointerException("Closer must not be null"); + else if (subgroup == null) throw new NullPointerException("Subgroup must not be null"); + else if (!closingDelimiters.contains(closer)) throw new IllegalArgumentException(String.format("No closing delimiter '%s' defined", closer)); + else if (!subgroups.containsKey(subgroup)) throw new IllegalArgumentException(String.format("No subgroup '%s' defined", subgroup)); + + impliedSubgroups.put(closer, subgroup); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + + builder.append("("); + + builder.append("groupName=["); + builder.append(groupName); + builder.append("], "); + + builder.append("closingDelimiters=["); + for (final T closer : closingDelimiters) { + builder.append(closer + ","); + } + builder.deleteCharAt(builder.length() - 1); + builder.append("]"); + + if (topLevelExclusions != null && !topLevelExclusions.isEmpty()) { + builder.append(", "); + builder.append("topLevelExclusions=["); + for (final T exclusion : topLevelExclusions) { + builder.append(exclusion + ","); + } + builder.deleteCharAt(builder.length() - 1); + builder.append("]"); + } + + if (groupExclusions != null && !groupExclusions.isEmpty()) { + builder.append(", "); + builder.append("groupExclusions=["); + for (final T exclusion : groupExclusions) { + builder.append(exclusion + ","); + } + builder.deleteCharAt(builder.length() - 1); + builder.append("]"); + } + + builder.append(" )"); + + return builder.toString(); + } + + /** + * Open an instance of this group. + * + * @param opener + * The item that opened this group. + * + * @param parms + * The parameters that opened this group + * + * @return An opened instance of this group. + */ + public OpenGroup open(final T opener, final T[] parms) { + return new OpenGroup(opener, parms); + } + + /** + * Adds a predicated opener to the top level of this group. + * + * @param pred + * The predicate that defines the opener and its + * parameters. + */ + public void addPredOpener(final Function> pred) { + predOpeners.add(pred); + } + + /** + * Adds a predicated closer to the top level of this group. + * + * @param pred + * The predicate that defines the closer. + */ + public void addPredCloser(final BiPredicate pred) { + predClosers.add(pred); + } + + /** + * Set whether or not this group starts a new nesting set. + * + * @param forgetful + * Whether this group starts a new nesting set. + */ + public void setForgetful(final boolean forgetful) { + this.forgetful = forgetful; + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/delims/RegexCloser.java b/base/src/main/java/bjc/utils/parserutils/delims/RegexCloser.java new file mode 100644 index 0000000..4b29949 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/delims/RegexCloser.java @@ -0,0 +1,33 @@ +package bjc.utils.parserutils.delims; + +import java.util.function.BiPredicate; + +/** + * A predicated closer for use with {@link RegexOpener}. + * + * @author bjculkin + * + */ +public class RegexCloser implements BiPredicate { + private final String rep; + + /** + * Create a new regex closer. + * + * @param closer + * The format string to use for closing. + */ + public RegexCloser(final String closer) { + rep = closer; + } + + @Override + public boolean test(final String closer, final String[] params) { + /* + * Confirm passing an array instead of a single var-arg. + */ + final String work = String.format(rep, (Object[]) params); + + return work.equals(closer); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java b/base/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java new file mode 100644 index 0000000..ee93b73 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/delims/RegexOpener.java @@ -0,0 +1,54 @@ +package bjc.utils.parserutils.delims; + +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import bjc.utils.data.IPair; +import bjc.utils.data.Pair; + +/** + * A predicated opener for use with {@link RegexCloser} + * + * @author bjculkin + * + */ +public class RegexOpener implements Function> { + private final String name; + + private final Pattern patt; + + /** + * Create a new regex opener. + * + * @param groupName + * The name of the opened group. + * + * @param groupRegex + * The regex that matches the opener. + */ + public RegexOpener(final String groupName, final String groupRegex) { + name = groupName; + + patt = Pattern.compile(groupRegex); + } + + @Override + public IPair apply(final String str) { + final Matcher m = patt.matcher(str); + + if (m.matches()) { + final int numGroups = m.groupCount(); + + final String[] parms = new String[numGroups + 1]; + + for (int i = 0; i <= numGroups; i++) { + parms[i] = m.group(i); + } + + return new Pair<>(name, parms); + } + + return new Pair<>(null, null); + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/delims/SequenceCharacteristics.java b/base/src/main/java/bjc/utils/parserutils/delims/SequenceCharacteristics.java new file mode 100644 index 0000000..882b4c5 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/delims/SequenceCharacteristics.java @@ -0,0 +1,93 @@ +package bjc.utils.parserutils.delims; + +/** + * Marks the parameters for building a sequence tree. + * + * @author EVE + * + * @param + * The type of item in the tree. + */ +public class SequenceCharacteristics { + /** + * The item to mark the root of the tree. + */ + public final T root; + + /** + * The item to mark the contents of a group/subgroup. + */ + + public final T contents; + + /** + * The item to mark a subgroup. + */ + public final T subgroup; + + /** + * Create a new set of parameters for building a tree. + * + * @param root + * The root marker. + * @param contents + * The group/subgroup contents marker. + * @param subgroup + * The subgroup marker. + */ + public SequenceCharacteristics(final T root, final T contents, final T subgroup) { + this.root = root; + this.contents = contents; + this.subgroup = subgroup; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + result = prime * result + (contents == null ? 0 : contents.hashCode()); + result = prime * result + (root == null ? 0 : root.hashCode()); + result = prime * result + (subgroup == null ? 0 : subgroup.hashCode()); + + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof SequenceCharacteristics)) return false; + + final SequenceCharacteristics other = (SequenceCharacteristics) obj; + + if (contents == null) { + if (other.contents != null) return false; + } else if (!contents.equals(other.contents)) return false; + + if (root == null) { + if (other.root != null) return false; + } else if (!root.equals(other.root)) return false; + + if (subgroup == null) { + if (other.subgroup != null) return false; + } else if (!subgroup.equals(other.subgroup)) return false; + + return true; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + + builder.append("SequenceCharacteristics [root="); + builder.append(root == null ? "(null)" : root); + builder.append(", contents="); + builder.append(contents == null ? "(null)" : contents); + builder.append(", subgroup="); + builder.append(subgroup == null ? "(null)" : subgroup); + builder.append("]"); + + return builder.toString(); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java b/base/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java new file mode 100644 index 0000000..ccfaffb --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/delims/SequenceDelimiter.java @@ -0,0 +1,371 @@ +package bjc.utils.parserutils.delims; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multiset; + +import bjc.utils.data.IPair; +import bjc.utils.data.ITree; +import bjc.utils.data.Tree; +import bjc.utils.esodata.PushdownMap; +import bjc.utils.esodata.SimpleStack; +import bjc.utils.esodata.Stack; +import bjc.utils.funcdata.IMap; +import bjc.utils.funcutils.StringUtils; + +/** + * Convert linear sequences into trees that represent group structure. + * + * @author EVE + * + * @param + * The type of items in the sequence. + */ +public class SequenceDelimiter { + /* + * Mapping from group names to actual groups. + */ + private final Map> groups; + + /* + * The initial group to start with. + */ + private DelimiterGroup initialGroup; + + /** + * Create a new sequence delimiter. + */ + public SequenceDelimiter() { + groups = new HashMap<>(); + } + + /** + * Convert a linear sequence into a tree that matches the delimiter + * structure. + * + * Essentially, creates a parse tree of the expression against the + * following grammar while obeying the defined grouping rules. + * + *
+	 *              → ( |  | )*
+	 *          
+	 *           
+	 *
+	 *              → STRING
+	 *              → STRING
+	 *             → STRING
+	 *            → STRING
+	 * 
+ * + * @param chars + * The parameters on how to mark certain portions of the + * tree. + * @param seq + * The sequence to delimit. + * + * @return The sequence as a tree that matches its group structure. Each + * node in the tree is either a data node, a subgroup node, or a + * group node. + * + * A data node is a leaf node whose data is the string it + * represents. + * + * A subgroup node is a node with two children, and the name of + * the sub-group as its label. The first child is the contents + * of the sub-group, and the second is the marker that started + * the subgroup. The marker is a leaf node labeled with its + * contents, and the contents contains a recursive tree. + * + * A group node is a node with three children, and the name of + * the group as its label. The first child is the opening + * delimiter, the second is the group contents, and the third is + * the closing delimiter. The delimiters are leaf nodes labeled + * with their contents, while the group node contains a + * recursive tree. + * + * @throws DelimiterException + * Thrown if something went wrong during sequence + * delimitation. + * + */ + public ITree delimitSequence(final SequenceCharacteristics chars, + @SuppressWarnings("unchecked") final T... seq) throws DelimiterException { + if (initialGroup == null) throw new NullPointerException("Initial group must be specified."); + else if (chars == null) throw new NullPointerException("Sequence characteristics must not be null"); + + /* + * The stack of opened and not yet closed groups. + */ + final Stack.OpenGroup> groupStack = new SimpleStack<>(); + + /* + * Open initial group. + */ + groupStack.push(initialGroup.open(chars.root, null)); + + /* + * Groups that aren't allowed to be opened at the moment. + */ + final Stack> forbiddenDelimiters = new SimpleStack<>(); + forbiddenDelimiters.push(HashMultiset.create()); + + /* + * Groups that are allowed to be opened at the moment. + */ + final Stack> allowedDelimiters = new SimpleStack<>(); + allowedDelimiters.push(HashMultimap.create()); + + /* + * Map of who forbid what for debugging purposes. + */ + final IMap whoForbid = new PushdownMap<>(); + + /* + * Process each member of the sequence. + */ + for (int i = 0; i < seq.length; i++) { + final T tok = seq[i]; + + /* + * Check if this token could open a group. + */ + final IPair possibleOpenPar = groupStack.top().doesOpen(tok); + T possibleOpen = possibleOpenPar.getLeft(); + + if (possibleOpen == null) { + /* + * Handle nested openers. + * + * Local openers take priority over nested ones + * if they overlap. + */ + if (allowedDelimiters.top().containsKey(tok)) { + possibleOpen = allowedDelimiters.top().get(tok).iterator().next(); + } + } + + /* + * If we have an opening delimiter, handle it. + */ + if (possibleOpen != null) { + final DelimiterGroup group = groups.get(possibleOpen); + + /* + * Error on groups that can't open in this + * context. + * + * This means groups that can't occur at the + * top-level of this group, as well as nested + * exclusions from all enclosing groups. + */ + if (isForbidden(groupStack, forbiddenDelimiters, possibleOpen)) { + T forbiddenBy; + + if (whoForbid.containsKey(tok)) { + forbiddenBy = whoForbid.get(tok); + } else { + forbiddenBy = groupStack.top().getName(); + } + + final String ctxList = StringUtils.toEnglishList(groupStack.toArray(), "then"); + + final String fmt = "Group '%s' can't be opened in this context. (forbidden by '%s')\nContext Stack: %s"; + + throw new DelimiterException(String.format(fmt, group, forbiddenBy, ctxList)); + } + + /* + * Add an open group. + */ + final DelimiterGroup.OpenGroup open = group.open(tok, possibleOpenPar.getRight()); + groupStack.push(open); + + /* + * Handle 'forgetful' groups that reset nesting + */ + if (open.isForgetful()) { + allowedDelimiters.push(HashMultimap.create()); + forbiddenDelimiters.push(HashMultiset.create()); + } + + /* + * Add the nested opens from this group. + */ + final Multimap currentAllowed = allowedDelimiters.top(); + for (final Entry opener : open.getNestingOpeners().entrySet()) { + currentAllowed.put(opener.getKey(), opener.getValue()); + } + + /* + * Add the nested exclusions from this group + */ + final Multiset currentForbidden = forbiddenDelimiters.top(); + for (final T exclusion : open.getNestingExclusions()) { + currentForbidden.add(exclusion); + + whoForbid.put(exclusion, possibleOpen); + } + } else if (!groupStack.empty() && groupStack.top().isClosing(tok)) { + /* + * Close the group. + */ + final DelimiterGroup.OpenGroup closed = groupStack.pop(); + + groupStack.top().addItem(closed.toTree(tok, chars)); + + /* + * Remove nested exclusions from this group. + */ + final Multiset currentForbidden = forbiddenDelimiters.top(); + for (final T excludedGroup : closed.getNestingExclusions()) { + currentForbidden.remove(excludedGroup); + + whoForbid.remove(excludedGroup); + } + + /* + * Remove the nested opens from this group. + */ + final Multimap currentAllowed = allowedDelimiters.top(); + for (final Entry closer : closed.getNestingOpeners().entrySet()) { + currentAllowed.remove(closer.getKey(), closer.getValue()); + } + + /* + * Handle 'forgetful' groups that reset nesting. + */ + if (closed.isForgetful()) { + allowedDelimiters.drop(); + forbiddenDelimiters.drop(); + } + } else if (!groupStack.empty() && groupStack.top().marksSubgroup(tok)) { + /* + * Mark a subgroup. + */ + groupStack.top().markSubgroup(tok, chars); + } else { + /* + * Add an item to the group. + */ + groupStack.top().addItem(new Tree<>(tok)); + } + } + + /* + * Error if not all groups were closed. + */ + if (groupStack.size() > 1) { + final DelimiterGroup.OpenGroup group = groupStack.top(); + + final StringBuilder msgBuilder = new StringBuilder(); + + final String closingDelims = StringUtils.toEnglishList(group.getNestingExclusions().toArray(), + false); + + final String ctxList = StringUtils.toEnglishList(groupStack.toArray(), "then"); + + msgBuilder.append("Unclosed group '"); + msgBuilder.append(group.getName()); + msgBuilder.append("'. Expected one of "); + msgBuilder.append(closingDelims); + msgBuilder.append(" to close it\nOpen groups: "); + msgBuilder.append(ctxList); + + final String fmt = "Unclosed group '%s'. Expected one of %s to close it.\nOpen groups: %n"; + + throw new DelimiterException(String.format(fmt, group.getName(), closingDelims, ctxList)); + } + + return groupStack.pop().toTree(chars.root, chars); + } + + /* + * Check if a group is forbidden to open in a context. + */ + private boolean isForbidden(final Stack.OpenGroup> groupStack, + final Stack> forbiddenDelimiters, final T groupName) { + boolean localForbid; + + /* + * Check if a delimiter is locally forbidden. + */ + if (groupStack.empty()) { + localForbid = false; + } else { + localForbid = groupStack.top().excludes(groupName); + } + + return localForbid || forbiddenDelimiters.top().contains(groupName); + } + + /** + * Add a delimiter group. + * + * @param group + * The delimiter group. + */ + public void addGroup(final DelimiterGroup group) { + if (group == null) throw new NullPointerException("Group must not be null"); + + groups.put(group.groupName, group); + } + + /** + * Creates and adds a delimiter group using the provided settings. + * + * @param openers + * The tokens that open this group + * @param groupName + * The name of the group + * @param closers + * The tokens that close this group + */ + public void addGroup(final T[] openers, final T groupName, @SuppressWarnings("unchecked") final T... closers) { + final DelimiterGroup group = new DelimiterGroup<>(groupName); + + group.addClosing(closers); + + addGroup(group); + + for (final T open : openers) { + group.addOpener(open, groupName); + } + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + + builder.append("SequenceDelimiter ["); + + if (groups != null) { + builder.append("groups="); + builder.append(groups); + builder.append(","); + } + + if (initialGroup != null) { + builder.append("initialGroup="); + builder.append(initialGroup); + } + + builder.append("]"); + + return builder.toString(); + } + + /** + * Set the initial group of this delimiter. + * + * @param initialGroup + * The initial group of this delimiter. + */ + public void setInitialGroup(final DelimiterGroup initialGroup) { + this.initialGroup = initialGroup; + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/delims/StringDelimiter.java b/base/src/main/java/bjc/utils/parserutils/delims/StringDelimiter.java new file mode 100644 index 0000000..e3eeea5 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/delims/StringDelimiter.java @@ -0,0 +1,31 @@ +package bjc.utils.parserutils.delims; + +import bjc.utils.data.ITree; + +/** + * A sequence delimiter specialized for strings. + * + * @author EVE + * + */ +public class StringDelimiter extends SequenceDelimiter { + + /** + * Override of + * {@link SequenceDelimiter#delimitSequence(SequenceCharacteristics, Object...)} + * for ease of use for strings. + * + * @param seq + * The sequence to delimit. + * + * @return The sequence as a tree. + * + * @throws DelimiterException + * if something went wrong with delimiting the sequence. + * + * @see SequenceDelimiter + */ + public ITree delimitSequence(final String... seq) throws DelimiterException { + return super.delimitSequence(new SequenceCharacteristics<>("root", "contents", "subgroup"), seq); + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/splitter/ChainTokenSplitter.java b/base/src/main/java/bjc/utils/parserutils/splitter/ChainTokenSplitter.java new file mode 100644 index 0000000..4736310 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/splitter/ChainTokenSplitter.java @@ -0,0 +1,50 @@ +package bjc.utils.parserutils.splitter; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * A token splitter that chains several other splitters together. + * + * @author EVE + * + */ +public class ChainTokenSplitter implements TokenSplitter { + private final IList spliters; + + /** + * Create a new chain token splitter. + */ + public ChainTokenSplitter() { + spliters = new FunctionalList<>(); + } + + /** + * Append a series of splitters to the chain. + * + * @param splitters + * The splitters to append to the chain. + */ + public void appendSplitters(final TokenSplitter... splitters) { + spliters.addAll(splitters); + } + + /** + * Prepend a series of splitters to the chain. + * + * @param splitters + * The splitters to append to the chain. + */ + public void prependSplitters(final TokenSplitter... splitters) { + spliters.prependAll(splitters); + } + + @Override + public IList split(final String input) { + final IList initList = new FunctionalList<>(input); + + return spliters.reduceAux(initList, (splitter, strangs) -> { + return strangs.flatMap(splitter::split); + }); + } +} \ No newline at end of file diff --git a/base/src/main/java/bjc/utils/parserutils/splitter/ConfigurableTokenSplitter.java b/base/src/main/java/bjc/utils/parserutils/splitter/ConfigurableTokenSplitter.java new file mode 100644 index 0000000..48ddcb4 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/splitter/ConfigurableTokenSplitter.java @@ -0,0 +1,122 @@ +package bjc.utils.parserutils.splitter; + +import static bjc.utils.PropertyDB.applyFormat; + +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.regex.Pattern; + +import bjc.utils.funcdata.IList; + +/** + * Split a string into pieces around a regular expression, and offer an easy way + * to configure the regular expression. + * + * @author EVE + * + */ +public class ConfigurableTokenSplitter extends SimpleTokenSplitter { + private final Set simpleDelimiters; + private final Set multipleDelimiters; + private final Set rRawDelimiters; + + /** + * Create a new token splitter with blank configuration. + * + * @param keepDelims + * Whether or not to keep delimiters. + */ + public ConfigurableTokenSplitter(final boolean keepDelims) { + super(null, keepDelims); + + /* + * Use linked hash-sets to keep items in insertion order. + */ + simpleDelimiters = new LinkedHashSet<>(); + multipleDelimiters = new LinkedHashSet<>(); + rRawDelimiters = new LinkedHashSet<>(); + } + + /** + * Add a set of simple delimiters to this splitter. + * + * Simple delimiters match one occurrence of themselves as literals. + * + * @param simpleDelims + * The simple delimiters to add. + */ + public void addSimpleDelimiters(final String... simpleDelims) { + for (final String simpleDelim : simpleDelims) { + simpleDelimiters.add(simpleDelim); + } + } + + /** + * Add a set of multiple delimiters to this splitter. + * + * Multiple delimiters match one or more occurrences of themselves as + * literals. + * + * @param multiDelims + * The multiple delimiters to add. + */ + public void addMultiDelimiters(final String... multiDelims) { + for (final String multiDelim : multiDelims) { + multipleDelimiters.add(multiDelim); + } + } + + /** + * Add a set of raw delimiters to this splitter. + * + * Raw delimiters match one occurrence of themselves as regular + * expressions. + * + * @param rRawDelims + * The raw delimiters to add. + */ + public void addRawDelimiters(final String... rRawDelims) { + for (final String rRawDelim : rRawDelims) { + rRawDelimiters.add(rRawDelim); + } + } + + /** + * Take the configuration and compile it into a regular expression to + * use when splitting. + */ + public void compile() { + final StringBuilder rPattern = new StringBuilder(); + + for (final String rRawDelimiter : rRawDelimiters) { + rPattern.append(applyFormat("rawDelim", rRawDelimiter)); + } + + for (final String multipleDelimiter : multipleDelimiters) { + rPattern.append(applyFormat("multipleDelim", multipleDelimiter)); + } + + for (final String simpleDelimiter : simpleDelimiters) { + rPattern.append(applyFormat("simpleDelim", simpleDelimiter)); + } + + rPattern.deleteCharAt(rPattern.length() - 1); + + spliter = Pattern.compile(rPattern.toString()); + } + + @Override + public IList split(final String input) { + if (spliter == null) throw new IllegalStateException("Must compile splitter before use"); + + return super.split(input); + } + + @Override + public String toString() { + final String fmt = "ConfigurableTokenSplitter [simpleDelimiters=%s, multipleDelimiters=%s," + + " rRawDelimiters=%s, spliter=%s]"; + + return String.format(fmt, simpleDelimiters, multipleDelimiters, rRawDelimiters, spliter); + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/splitter/ExcludingTokenSplitter.java b/base/src/main/java/bjc/utils/parserutils/splitter/ExcludingTokenSplitter.java new file mode 100644 index 0000000..369e7ae --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/splitter/ExcludingTokenSplitter.java @@ -0,0 +1,71 @@ +package bjc.utils.parserutils.splitter; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Predicate; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.IList; + +/** + * A token splitter that will not split certain tokens. + * + * @author EVE + * + */ +public class ExcludingTokenSplitter implements TokenSplitter { + private final Set literalExclusions; + + private final IList> predExclusions; + + private final TokenSplitter spliter; + + /** + * Create a new excluding token splitter. + * + * @param splitter + * The splitter to apply to non-excluded strings. + */ + public ExcludingTokenSplitter(final TokenSplitter splitter) { + spliter = splitter; + + literalExclusions = new HashSet<>(); + + predExclusions = new FunctionalList<>(); + } + + /** + * Exclude literal strings from splitting. + * + * @param exclusions + * The strings to exclude from splitting. + */ + public final void addLiteralExclusions(final String... exclusions) { + for (final String exclusion : exclusions) { + literalExclusions.add(exclusion); + } + } + + /** + * Exclude all of the strings matching any of the predicates from + * splitting. + * + * @param exclusions + * The predicates to use for exclusions. + */ + @SafeVarargs + public final void addPredicateExclusion(final Predicate... exclusions) { + for (final Predicate exclusion : exclusions) { + predExclusions.add(exclusion); + } + } + + @Override + public IList split(final String input) { + if (literalExclusions.contains(input)) + return new FunctionalList<>(input); + else if (predExclusions.anyMatch(pred -> pred.test(input))) + return new FunctionalList<>(input); + else return spliter.split(input); + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/splitter/FilteredTokenSplitter.java b/base/src/main/java/bjc/utils/parserutils/splitter/FilteredTokenSplitter.java new file mode 100644 index 0000000..5d954e0 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/splitter/FilteredTokenSplitter.java @@ -0,0 +1,37 @@ +package bjc.utils.parserutils.splitter; + +import java.util.function.Predicate; + +import bjc.utils.funcdata.IList; + +/** + * A token splitter that removes tokens that match a predicate from the stream + * of tokens. + * + * @author bjculkin + * + */ +public class FilteredTokenSplitter implements TokenSplitter { + private TokenSplitter source; + + private Predicate filter; + + /** + * Create a new filtered token splitter. + * + * @param source + * The splitter to get tokens from. + * + * @param filter + * The filter to pass tokens through. + */ + public FilteredTokenSplitter(TokenSplitter source, Predicate filter) { + this.source = source; + this.filter = filter; + } + + @Override + public IList split(String input) { + return source.split(input).getMatching(filter); + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java b/base/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java new file mode 100644 index 0000000..c357886 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/splitter/SimpleTokenSplitter.java @@ -0,0 +1,46 @@ +package bjc.utils.parserutils.splitter; + +import java.util.regex.Pattern; + +import bjc.utils.funcdata.IList; +import bjc.utils.functypes.ID; +import bjc.utils.ioutils.RegexStringEditor; + +/** + * Splits a string into pieces around a regular expression. + * + * @author EVE + * + */ +public class SimpleTokenSplitter implements TokenSplitter { + protected Pattern spliter; + + private final boolean keepDelim; + + /** + * Create a new simple token splitter. + * + * @param splitter + * The pattern to split around. + * + * @param keepDelims + * Whether or not delimiters should be kept. + */ + public SimpleTokenSplitter(final Pattern splitter, final boolean keepDelims) { + spliter = splitter; + + keepDelim = keepDelims; + } + + @Override + public IList split(final String input) { + if (keepDelim) + return RegexStringEditor.mapOccurances(input, spliter, ID.id(), ID.id()); + else return RegexStringEditor.mapOccurances(input, spliter, ID.id(), strang -> ""); + } + + @Override + public String toString() { + return String.format("SimpleTokenSplitter [spliter=%s, keepDelim=%s]", spliter, keepDelim); + } +} diff --git a/base/src/main/java/bjc/utils/parserutils/splitter/TokenSplitter.java b/base/src/main/java/bjc/utils/parserutils/splitter/TokenSplitter.java new file mode 100644 index 0000000..ddb28a7 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/splitter/TokenSplitter.java @@ -0,0 +1,21 @@ +package bjc.utils.parserutils.splitter; + +import bjc.utils.funcdata.IList; + +/** + * Split a string into a list of pieces. + * + * @author EVE + * + */ +public interface TokenSplitter { + /** + * Split a string into a list of pieces. + * + * @param input + * The string to split. + * + * @return The pieces of the string. + */ + public IList split(String input); +} diff --git a/base/src/main/java/bjc/utils/parserutils/splitter/TransformTokenSplitter.java b/base/src/main/java/bjc/utils/parserutils/splitter/TransformTokenSplitter.java new file mode 100644 index 0000000..80490f5 --- /dev/null +++ b/base/src/main/java/bjc/utils/parserutils/splitter/TransformTokenSplitter.java @@ -0,0 +1,38 @@ +package bjc.utils.parserutils.splitter; + +import java.util.function.UnaryOperator; + +import bjc.utils.funcdata.IList; + +/** + * A token splitter that performs a transform on the tokens from another + * splitter. + * + * @author bjculkin + * + */ +public class TransformTokenSplitter implements TokenSplitter { + private TokenSplitter source; + + private UnaryOperator transform; + + /** + * Create a new transforming splitter. + * + * @param source + * The splitter to use as a source. + * + * @param transform + * The transform to apply to tokens. + */ + public TransformTokenSplitter(TokenSplitter source, UnaryOperator transform) { + this.source = source; + this.transform = transform; + } + + @Override + public IList split(String input) { + return source.split(input).map(transform); + } + +} diff --git a/base/src/test/java/bjc/utils/test/parserutils/TokenUtilsTest.java b/base/src/test/java/bjc/utils/test/parserutils/TokenUtilsTest.java new file mode 100644 index 0000000..6fba1b2 --- /dev/null +++ b/base/src/test/java/bjc/utils/test/parserutils/TokenUtilsTest.java @@ -0,0 +1,152 @@ +package bjc.utils.test.parserutils; + +import static bjc.utils.parserutils.TokenUtils.descapeString; +import static bjc.utils.parserutils.TokenUtils.removeDQuotedStrings; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.List; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +/* + * Tests for TokenUtils + */ +public class TokenUtilsTest { + @Rule + public ExpectedException exp = ExpectedException.none(); + + /* + * Test removeDQuoted + */ + + /* + * Check handling of mismatched strings with no matching strings. + */ + @Test + public void testRemoveDQuoted_MismatchedStringNoMatch() throws IllegalArgumentException { + exp.expect(IllegalArgumentException.class); + exp.expectMessage(containsString("Opening quote was at position 0")); + + removeDQuotedStrings("\"hello"); + } + + /* + * Check handling of mismatched strings with a matching string. + */ + @Test + public void testRemoveDQuoted_MismatchedStringMatch() throws IllegalArgumentException { + exp.expect(IllegalArgumentException.class); + exp.expectMessage(containsString("Opening quote was at position 7")); + + removeDQuotedStrings("\"hello\"\""); + } + + /* + * Check handling of strings with a single embedded string. + */ + @Test + public void testRemoveDQuoted_SingleString() { + final List onSingleMatchString = removeDQuotedStrings("hello\"there\""); + + assertThat(onSingleMatchString, hasItems("hello", "\"there\"")); + } + + /* + * Check handling of strings with multiple quoted strings in a row. + */ + @Test + public void testRemoveDQuoted_MultipleSerialString() { + final List onMultipleSerialMatchString = removeDQuotedStrings("\"hello\"\"there\""); + + assertThat(onMultipleSerialMatchString, hasItems("\"hello\"", "\"there\"")); + } + + /* + * Check handling of strings with multiple interleaved strings. + */ + @Test + public void testRemoveDQuoted_MultipleInterleavedString() { + final List onMultipleInterleaveMatchString = removeDQuotedStrings("one\"two\"three\"four\""); + + assertThat(onMultipleInterleaveMatchString, hasItems("one", "\"two\"", "three", "\"four\"")); + } + + /* + * Check handling of strings without embedded strings. + */ + @Test + public void testRemoveDQuote_NoString() { + final List onNonmatchingString = removeDQuotedStrings("hello"); + + assertThat(onNonmatchingString, hasItems("hello")); + } + + /* + * Check handling of empty strings. + */ + @Test + public void testRemoveDQuote_EmptyString() { + final List onEmptyString = removeDQuotedStrings(""); + + assertThat(onEmptyString, hasItems("")); + } + + /* + * Test descapeString + */ + /* + * Check handling of empty strings. + */ + @Test + public void testDescapeString_EmptyString() { + final String onEmptyString = descapeString(""); + + assertThat(onEmptyString, is("")); + } + + /* + * Check handling of strings without escapes + */ + @Test + public void testDescapeString_NonescapeString() { + final String onNonescapeString = descapeString("hello there"); + + assertThat(onNonescapeString, is("hello there")); + } + + /* + * Check handling of strings with single escapes. + */ + @Test + public void testDescapeString_SingleEscapeString() { + final String onSingleEscapeString = descapeString("hello\\tthere"); + + assertThat(onSingleEscapeString, is("hello\tthere")); + } + + /* + * Check handling of strings with multiple escapes. + */ + @Test + public void testDescapeString_MultipleEscapeString() { + final String onMultipleEscapeString = descapeString("hello\\tthere\\tworld"); + + assertThat(onMultipleEscapeString, is("hello\tthere\tworld")); + } + + /* + * Check handling of strings with invalid single escapes. + */ + @Test + public void testDescapeString_InvalidSingleEscapeString() throws IllegalArgumentException { + exp.expect(IllegalArgumentException.class); + exp.expectMessage(containsString("at position 0")); + + descapeString("\\x"); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..cf6ec24 --- /dev/null +++ b/pom.xml @@ -0,0 +1,13 @@ + + 4.0.0 + + bjc + BJCUtils-Parent + 1.0.0 + pom + + + base + + diff --git a/todos.txt b/todos.txt new file mode 100644 index 0000000..eb134a1 --- /dev/null +++ b/todos.txt @@ -0,0 +1,4 @@ +@TODO 10/08/17 Ben Culkin :ModuleSplitting + Split BJC-Utils into multiple maven modules. That'll cut down on + compilation times and also the amount of dependencies pulled in for + certain things. -- cgit v1.2.3