[tree] Add TreeBuilder (#57)
* [tree] TreeBuilder: whenEmptyBuilderBuildThenTreeIsAnEmptyLeaf() * [tree] Provide default implementation of count() * [tree] Extract TreeMapper * [tree] MutableTree added * [tree] TreeBuilder: whenAddLeafThenTreeHasLeaf() * [tree] TreeBuilder: whenAddSubTreeThenTreeHasSubTree() * [maybe] Add Maybe.findFirst(Stream) and matchValue(Function, Supplier) * [tree] TreeBuilder: whenAddGrandChildThenTreeHasGrandChild() * [tree] TreeBuilder: whenAddMultipleChildrenThenTreeHasAllChildren() * [tree] Tree: avoid leaking MutableTree type as parameters * [changelog] update * [changelog][readme] Update to include TreeBuilder
This commit is contained in:
parent
73e59b0050
commit
8357d7f5d8
17 changed files with 986 additions and 36 deletions
|
@ -1,9 +1,13 @@
|
|||
CHANGELOG
|
||||
=========
|
||||
|
||||
1.2.1
|
||||
2.0.0
|
||||
-----
|
||||
|
||||
* [BREAKING] [tree] Tree.item() now returns a Maybe
|
||||
* [tree] Add TreeBuilder
|
||||
* [maybe] Add static Maybe.findFirst(Stream)
|
||||
* [maybe] Add matchValue(Function, Supplier)
|
||||
* Bump kemitix-parent from 5.1.1 to 5.2.0 (#55)
|
||||
* Bump junit from 4.12 to 5.3.1 [#56)
|
||||
|
||||
|
|
130
README.org
130
README.org
|
@ -255,7 +255,7 @@
|
|||
have a =get()= method.
|
||||
|
||||
Unlike =Optional=, when a =map()= results in a =null=, the =Maybe= will
|
||||
continue to be a =Just=. =Optional= would switch to being empty. [[http://blog.vavr.io/the-agonizing-death-of-an-astronaut/][vavi.io
|
||||
continue to be a =Just=. =Optional= would switch to being empty. [[http://blog.vavr.io/the-agonizing-death-of-an-astronaut/][vavr.io
|
||||
follows the same behaviour as =Maybe=]].
|
||||
|
||||
#+BEGIN_SRC java
|
||||
|
@ -385,6 +385,16 @@
|
|||
#+END_SRC
|
||||
|
||||
|
||||
**** =static <T> Maybe<T> findFirst(Stream<T> stream)=
|
||||
|
||||
Creates a Maybe from the first item in the stream, or nothing if the stream is empty.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
final Maybe<Integer> just3 = Maybe.findFirst(Stream.of(3, 4, 2, 4));
|
||||
final Maybe<Integer> nothing = Maybe.findFirst(Stream.empty());
|
||||
#+END_SRC
|
||||
|
||||
|
||||
*** Instance Methods
|
||||
|
||||
**** =Maybe<T> filter(Predicate<T> predicate)=
|
||||
|
@ -421,7 +431,8 @@
|
|||
|
||||
**** =void match(Consumer<T> just, Runnable nothing)=
|
||||
|
||||
Matches the Maybe, either just or nothing, and performs either the Consumer, for Just, or Runnable for nothing.
|
||||
Matches the Maybe, either just or nothing, and performs either the
|
||||
Consumer, for Just, or Runnable for nothing.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
Maybe.maybe(getValue())
|
||||
|
@ -432,6 +443,20 @@
|
|||
#+END_SRC
|
||||
|
||||
|
||||
**** =<R> R matchValue(Function<T, R> justMatcher, Supplier<R> nothingMatcher)=
|
||||
|
||||
Matches the Maybe, either just or nothing, and performs either the
|
||||
Function, for Just, or Supplier for nothing.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
final String value = Maybe.maybe(getValue())
|
||||
.matchValue(
|
||||
just -> Integer.toString(just),
|
||||
() -> "nothing"
|
||||
);
|
||||
#+END_SRC
|
||||
|
||||
|
||||
**** =T orElse(T otherValue)=
|
||||
|
||||
A value to use when Maybe is Nothing.
|
||||
|
@ -907,6 +932,25 @@
|
|||
#+END_SRC
|
||||
|
||||
|
||||
**** =static <B> TreeBuilder<B> builder(final Class<B> type)=
|
||||
|
||||
Create a new TreeBuilder starting with an empty tree.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
final TreeBuilder<Integer> builder = Tree.builder(Integer.class);
|
||||
#+END_SRC
|
||||
|
||||
|
||||
**** =static <B> TreeBuilder<B> builder(final Tree<B> tree)=
|
||||
|
||||
Create a new TreeBuilder for the given tree.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
final Tree<Integer> tree = ...;
|
||||
final TreeBuilder<Integer> builder = Tree.builder(tree);
|
||||
#+END_SRC
|
||||
|
||||
|
||||
*** Instance Methods
|
||||
|
||||
**** =<R> Tree<R> map(Function<T, R> f)=
|
||||
|
@ -920,13 +964,13 @@
|
|||
#+END_SRC
|
||||
|
||||
|
||||
**** =Optional<T> item()=
|
||||
**** =Maybe<T> item()=
|
||||
|
||||
Returns the contents of the Tree node within an Optional.
|
||||
Returns the contents of the Tree node within a Maybe.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
final Tree<Item> tree = ...;
|
||||
final Optional<Item> result = tree.item();
|
||||
final Maybe<Item> result = tree.item();
|
||||
#+END_SRC
|
||||
|
||||
|
||||
|
@ -951,6 +995,82 @@
|
|||
#+END_SRC
|
||||
|
||||
|
||||
** TreeBuilder
|
||||
|
||||
A mutable builder for a Tree. Each TreeBuilder allows modification of a
|
||||
single Tree node. You can use the =select(childItem)= method to get a
|
||||
TreeBuilder for the subtree that has the given child item.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
final TreeBuilder<Integer> builder = Tree.builder();
|
||||
builder.set(12).addChildren(Arrays.asList(1, 3, 5, 7));
|
||||
final TreeBuilder<Integer> builderFor3 = builder.select(3);
|
||||
builderFor3.addChildren(Arrays.asList(2, 4));
|
||||
final Tree<Integer> tree = builder.build();
|
||||
#+END_SRC
|
||||
|
||||
Will produce a tree like:
|
||||
|
||||
[[file:images/treebuilder-example.svg]]
|
||||
|
||||
|
||||
*** Static Constructors
|
||||
|
||||
None. The TreeBuilder is instantiated by =Tree.builder()=.
|
||||
|
||||
*** Instance Methods
|
||||
|
||||
**** =Tree<T> build()=
|
||||
|
||||
Create the immutable Tree.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
final TreeBuilder<Integer> builder = Tree.builder();
|
||||
final Tree<Integer> tree = builder.build();
|
||||
#+END_SRC
|
||||
|
||||
|
||||
**** =TreeBuilder<T> item(T item)=
|
||||
|
||||
Set the current Tree's item and return the TreeBuilder.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
#+END_SRC
|
||||
|
||||
|
||||
**** =TreeBuilder<T> add(Tree<T> subtree)=
|
||||
|
||||
Adds the subtree to the current tree.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
#+END_SRC
|
||||
|
||||
|
||||
**** =TreeBuilder<T> addChild(T childItem)=
|
||||
|
||||
Add the Child item as a sub-Tree.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
#+END_SRC
|
||||
|
||||
|
||||
**** =TreeBuilder<T> addChildren(List<T> children)=
|
||||
|
||||
Add all the child items as subTrees.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
#+END_SRC
|
||||
|
||||
|
||||
**** =Maybe<TreeBuilder<T>> select(T childItem)=
|
||||
|
||||
Create a TreeBuilder for the subTree of the current Tree that has the
|
||||
childItem.
|
||||
|
||||
#+BEGIN_SRC java
|
||||
#+END_SRC
|
||||
|
||||
|
||||
** Lazy
|
||||
|
||||
A Lazy evaluated expression. Using a Supplier to provide the value, only
|
||||
|
|
8
images/treebuilder-example.dot
Normal file
8
images/treebuilder-example.dot
Normal file
|
@ -0,0 +1,8 @@
|
|||
digraph G {
|
||||
12 -> 1;
|
||||
12 -> 3;
|
||||
12 -> 5;
|
||||
12 -> 7;
|
||||
3 -> 2;
|
||||
3 -> 4;
|
||||
}
|
91
images/treebuilder-example.svg
Normal file
91
images/treebuilder-example.svg
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
|
||||
-->
|
||||
<!-- Title: G Pages: 1 -->
|
||||
<svg width="278pt" height="188pt"
|
||||
viewBox="0.00 0.00 278.00 188.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 184)">
|
||||
<title>G</title>
|
||||
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-184 274,-184 274,4 -4,4"/>
|
||||
<!-- 12 -->
|
||||
<g id="node1" class="node">
|
||||
<title>12</title>
|
||||
<ellipse fill="none" stroke="#000000" cx="135" cy="-162" rx="27" ry="18"/>
|
||||
<text text-anchor="middle" x="135" y="-158.3" font-family="Times,serif" font-size="14.00" fill="#000000">12</text>
|
||||
</g>
|
||||
<!-- 1 -->
|
||||
<g id="node2" class="node">
|
||||
<title>1</title>
|
||||
<ellipse fill="none" stroke="#000000" cx="27" cy="-90" rx="27" ry="18"/>
|
||||
<text text-anchor="middle" x="27" y="-86.3" font-family="Times,serif" font-size="14.00" fill="#000000">1</text>
|
||||
</g>
|
||||
<!-- 12->1 -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>12->1</title>
|
||||
<path fill="none" stroke="#000000" d="M115.6918,-149.1278C98.6445,-137.763 73.5981,-121.0654 54.4656,-108.3104"/>
|
||||
<polygon fill="#000000" stroke="#000000" points="56.4031,-105.3956 46.1411,-102.7607 52.5201,-111.2199 56.4031,-105.3956"/>
|
||||
</g>
|
||||
<!-- 3 -->
|
||||
<g id="node3" class="node">
|
||||
<title>3</title>
|
||||
<ellipse fill="none" stroke="#000000" cx="99" cy="-90" rx="27" ry="18"/>
|
||||
<text text-anchor="middle" x="99" y="-86.3" font-family="Times,serif" font-size="14.00" fill="#000000">3</text>
|
||||
</g>
|
||||
<!-- 12->3 -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>12->3</title>
|
||||
<path fill="none" stroke="#000000" d="M126.2854,-144.5708C122.0403,-136.0807 116.8464,-125.6929 112.1337,-116.2674"/>
|
||||
<polygon fill="#000000" stroke="#000000" points="115.237,-114.6477 107.6343,-107.2687 108.976,-117.7782 115.237,-114.6477"/>
|
||||
</g>
|
||||
<!-- 5 -->
|
||||
<g id="node4" class="node">
|
||||
<title>5</title>
|
||||
<ellipse fill="none" stroke="#000000" cx="171" cy="-90" rx="27" ry="18"/>
|
||||
<text text-anchor="middle" x="171" y="-86.3" font-family="Times,serif" font-size="14.00" fill="#000000">5</text>
|
||||
</g>
|
||||
<!-- 12->5 -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>12->5</title>
|
||||
<path fill="none" stroke="#000000" d="M143.7146,-144.5708C147.9597,-136.0807 153.1536,-125.6929 157.8663,-116.2674"/>
|
||||
<polygon fill="#000000" stroke="#000000" points="161.024,-117.7782 162.3657,-107.2687 154.763,-114.6477 161.024,-117.7782"/>
|
||||
</g>
|
||||
<!-- 7 -->
|
||||
<g id="node5" class="node">
|
||||
<title>7</title>
|
||||
<ellipse fill="none" stroke="#000000" cx="243" cy="-90" rx="27" ry="18"/>
|
||||
<text text-anchor="middle" x="243" y="-86.3" font-family="Times,serif" font-size="14.00" fill="#000000">7</text>
|
||||
</g>
|
||||
<!-- 12->7 -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>12->7</title>
|
||||
<path fill="none" stroke="#000000" d="M154.3082,-149.1278C171.3555,-137.763 196.4019,-121.0654 215.5344,-108.3104"/>
|
||||
<polygon fill="#000000" stroke="#000000" points="217.4799,-111.2199 223.8589,-102.7607 213.5969,-105.3956 217.4799,-111.2199"/>
|
||||
</g>
|
||||
<!-- 2 -->
|
||||
<g id="node6" class="node">
|
||||
<title>2</title>
|
||||
<ellipse fill="none" stroke="#000000" cx="63" cy="-18" rx="27" ry="18"/>
|
||||
<text text-anchor="middle" x="63" y="-14.3" font-family="Times,serif" font-size="14.00" fill="#000000">2</text>
|
||||
</g>
|
||||
<!-- 3->2 -->
|
||||
<g id="edge5" class="edge">
|
||||
<title>3->2</title>
|
||||
<path fill="none" stroke="#000000" d="M90.2854,-72.5708C86.0403,-64.0807 80.8464,-53.6929 76.1337,-44.2674"/>
|
||||
<polygon fill="#000000" stroke="#000000" points="79.237,-42.6477 71.6343,-35.2687 72.976,-45.7782 79.237,-42.6477"/>
|
||||
</g>
|
||||
<!-- 4 -->
|
||||
<g id="node7" class="node">
|
||||
<title>4</title>
|
||||
<ellipse fill="none" stroke="#000000" cx="135" cy="-18" rx="27" ry="18"/>
|
||||
<text text-anchor="middle" x="135" y="-14.3" font-family="Times,serif" font-size="14.00" fill="#000000">4</text>
|
||||
</g>
|
||||
<!-- 3->4 -->
|
||||
<g id="edge6" class="edge">
|
||||
<title>3->4</title>
|
||||
<path fill="none" stroke="#000000" d="M107.7146,-72.5708C111.9597,-64.0807 117.1536,-53.6929 121.8663,-44.2674"/>
|
||||
<polygon fill="#000000" stroke="#000000" points="125.024,-45.7782 126.3657,-35.2687 118.763,-42.6477 125.024,-45.7782"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.2 KiB |
|
@ -118,6 +118,14 @@ final class Just<T> implements Maybe<T> {
|
|||
justMatcher.accept(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> R matchValue(
|
||||
final Function<T, R> justMatcher,
|
||||
final Supplier<R> nothingMatcher
|
||||
) {
|
||||
return justMatcher.apply(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maybe<T> or(final Supplier<Maybe<T>> alternative) {
|
||||
return this;
|
||||
|
|
|
@ -81,6 +81,19 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
|
|||
return just(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Maybe from the first item in the stream, or nothing if the stream is empty.
|
||||
*
|
||||
* @param stream the Stream
|
||||
* @param <T> the type of the stream
|
||||
* @return a Maybe containing the first item in the stream
|
||||
*/
|
||||
public static <T> Maybe<T> findFirst(Stream<T> stream) {
|
||||
return stream.findFirst()
|
||||
.map(Maybe::just)
|
||||
.orElse(Maybe.nothing());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Maybe is a Just.
|
||||
*
|
||||
|
@ -173,11 +186,26 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
|
|||
/**
|
||||
* Matches the Maybe, either just or nothing, and performs either the Consumer, for Just, or Runnable for nothing.
|
||||
*
|
||||
* <p>Unlike {@link #matchValue(Function, Supplier)}, this method does not return a value.</p>
|
||||
*
|
||||
* @param justMatcher the Consumer to pass the value of a Just to
|
||||
* @param nothingMatcher the Runnable to call if the Maybe is a Nothing
|
||||
*/
|
||||
public abstract void match(Consumer<T> justMatcher, Runnable nothingMatcher);
|
||||
|
||||
/**
|
||||
* Matches the Maybe, either just or nothing, and performs either the Function, for Just, or Supplier for nothing.
|
||||
*
|
||||
* <p>Unlike {@link #match(Consumer, Runnable)}, this method returns a value.</p>
|
||||
*
|
||||
* @param justMatcher the Function to pass the value of a Just to
|
||||
* @param nothingMatcher the Supplier to call if the Maybe is a Nothing
|
||||
* @param <R> the type of the matched result
|
||||
*
|
||||
* @return the matched result, from either the justMatcher or the nothingMatcher
|
||||
*/
|
||||
public abstract <R> R matchValue(Function<T, R> justMatcher, Supplier<R> nothingMatcher);
|
||||
|
||||
/**
|
||||
* Maps the Maybe into another Maybe only when it is nothing.
|
||||
*
|
||||
|
|
|
@ -100,6 +100,14 @@ final class Nothing<T> implements Maybe<T> {
|
|||
nothingMatcher.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> R matchValue(
|
||||
final Function<T, R> justMatcher,
|
||||
final Supplier<R> nothingMatcher
|
||||
) {
|
||||
return nothingMatcher.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maybe<T> or(final Supplier<Maybe<T>> alternative) {
|
||||
return alternative.get();
|
||||
|
|
|
@ -22,10 +22,10 @@
|
|||
package net.kemitix.mon.tree;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import net.kemitix.mon.maybe.Maybe;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A generic tree of trees and objects.
|
||||
|
@ -37,7 +37,7 @@ import java.util.stream.Collectors;
|
|||
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
class GeneralisedTree<T> implements Tree<T> {
|
||||
class GeneralisedTree<T> implements Tree<T>, TreeMapper<T> {
|
||||
|
||||
private final T item;
|
||||
private final List<Tree<T>> subTrees;
|
||||
|
@ -62,30 +62,12 @@ class GeneralisedTree<T> implements Tree<T> {
|
|||
*/
|
||||
@Override
|
||||
public <R> Tree<R> map(final Function<T, R> f) {
|
||||
return Tree.of(f.apply(item), mapSubTrees(f));
|
||||
return Tree.of(f.apply(item), mapTrees(f, subTrees()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> item() {
|
||||
return Optional.ofNullable(item);
|
||||
}
|
||||
|
||||
private <R> List<Tree<R>> mapSubTrees(final Function<T, R> f) {
|
||||
return subTrees.stream()
|
||||
.map(subTree -> subTree.map(f::apply))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of items in the subtree.
|
||||
*
|
||||
* @return the sum of the subtrees, plus 1 if there is an item in this node
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("avoidinlineconditionals")
|
||||
public int count() {
|
||||
return (item != null ? 1 : 0)
|
||||
+ subTrees.stream().mapToInt(Tree::count).sum();
|
||||
public Maybe<T> item() {
|
||||
return Maybe.maybe(item);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
168
src/main/java/net/kemitix/mon/tree/MutableTree.java
Normal file
168
src/main/java/net/kemitix/mon/tree/MutableTree.java
Normal file
|
@ -0,0 +1,168 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Paul Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies
|
||||
* or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.kemitix.mon.tree;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import net.kemitix.mon.maybe.Maybe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A mutable {@link Tree}.
|
||||
*
|
||||
* @param <T> the type of the objects help in the tree
|
||||
*
|
||||
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
@SuppressWarnings("methodcount")
|
||||
class MutableTree<T> implements Tree<T>, TreeMapper<T> {
|
||||
|
||||
private final List<MutableTree<T>> mySubTrees = new ArrayList<>();
|
||||
|
||||
private T item;
|
||||
|
||||
/**
|
||||
* Create a new {@link MutableTree}.
|
||||
*
|
||||
* @param <B> the type of the {@link Tree}.
|
||||
*
|
||||
* @return the MutableTree
|
||||
*/
|
||||
static <B> MutableTree<B> create() {
|
||||
return new MutableTree<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new mutable tree to hold object of a type.
|
||||
*
|
||||
* @param item the item for the leaf node
|
||||
* @param <B> the type of the object to be held in the tree
|
||||
*
|
||||
* @return a empty mutable tree
|
||||
*/
|
||||
static <B> MutableTree<B> leaf(final B item) {
|
||||
return MutableTree.<B>create().set(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new mutable tree to hold the single item.
|
||||
*
|
||||
* @param item the item for the tree node
|
||||
* @param subtrees the subtrees of the tree node
|
||||
* @param <B> the type of the item
|
||||
* @return a leaf node of a generalised tree
|
||||
*/
|
||||
static <B> MutableTree<B> of(final B item, final Collection<MutableTree<B>> subtrees) {
|
||||
return MutableTree.<B>create().set(item).subTrees(subtrees);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate, or cast if possible, an existing Tree as a {@link MutableTree}.
|
||||
* @param tree the tree to duplicate/cast
|
||||
* @param <T> the type of the tree
|
||||
* @return the mutable tree
|
||||
*/
|
||||
static <T> MutableTree<T> of(final Tree<T> tree) {
|
||||
if (tree instanceof MutableTree) {
|
||||
return (MutableTree<T>) tree;
|
||||
}
|
||||
final T item = tree.item().orElse(null);
|
||||
final List<MutableTree<T>> subtrees = tree.subTrees()
|
||||
.stream().map(MutableTree::of).collect(Collectors.toList());
|
||||
return MutableTree.of(item, subtrees);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> MutableTree<R> map(final Function<T, R> f) {
|
||||
final MutableTree<R> mutableTree = MutableTree.create();
|
||||
final List<MutableTree<R>> trees = subTreesAsMutable().stream()
|
||||
.map(subTree -> subTree.map(f))
|
||||
.collect(Collectors.toList());
|
||||
return mutableTree
|
||||
.set(f.apply(item))
|
||||
.subTrees(trees);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maybe<T> item() {
|
||||
return Maybe.maybe(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item of the tree.
|
||||
*
|
||||
* @param newItem the new item
|
||||
*
|
||||
* @return the tree
|
||||
*/
|
||||
MutableTree<T> set(final T newItem) {
|
||||
this.item = newItem;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Tree<T>> subTrees() {
|
||||
final Stream<MutableTree<T>> mutableTreeStream = mySubTrees.stream();
|
||||
final Stream<Tree<T>> treeStream = mutableTreeStream.map(Tree.class::cast);
|
||||
final List<Tree<T>> treeList = treeStream.collect(Collectors.toList());
|
||||
return treeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subTrees of the tree.
|
||||
*
|
||||
* @param subTrees the subtrees
|
||||
* @return the tree
|
||||
*/
|
||||
MutableTree<T> subTrees(final Collection<MutableTree<T>> subTrees) {
|
||||
this.mySubTrees.clear();
|
||||
this.mySubTrees.addAll(subTrees);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the subtree to the existing subtrees.
|
||||
*
|
||||
* @param subtree the subtree
|
||||
* @return the current tree
|
||||
*/
|
||||
MutableTree<T> add(final Tree<T> subtree) {
|
||||
mySubTrees.add(MutableTree.of(subtree));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The mutable subtrees of the tree.
|
||||
*
|
||||
* @return a list of Trees
|
||||
*/
|
||||
List<MutableTree<T>> subTreesAsMutable() {
|
||||
return mySubTrees;
|
||||
}
|
||||
}
|
91
src/main/java/net/kemitix/mon/tree/MutableTreeBuilder.java
Normal file
91
src/main/java/net/kemitix/mon/tree/MutableTreeBuilder.java
Normal file
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Paul Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies
|
||||
* or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.kemitix.mon.tree;
|
||||
|
||||
import net.kemitix.mon.maybe.Maybe;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Builder for a {@link Tree}.
|
||||
*
|
||||
* @param <T> the type of the tree
|
||||
*
|
||||
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||
*/
|
||||
class MutableTreeBuilder<T> implements TreeBuilder<T> {
|
||||
|
||||
private final MutableTree<T> root;
|
||||
|
||||
/**
|
||||
* Create empty tree builder.
|
||||
*/
|
||||
MutableTreeBuilder() {
|
||||
root = MutableTree.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tree builder to work with the given tree.
|
||||
*
|
||||
* @param tree the tree to build upon
|
||||
*/
|
||||
MutableTreeBuilder(final MutableTree<T> tree) {
|
||||
root = tree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tree<T> build() {
|
||||
return root.map(Function.identity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeBuilder<T> item(final T item) {
|
||||
root.set(item);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeBuilder<T> add(final Tree<T> subtree) {
|
||||
root.add(subtree);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeBuilder<T> addChild(final T childItem) {
|
||||
root.add(MutableTree.leaf(childItem));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maybe<TreeBuilder<T>> select(final T childItem) {
|
||||
return Maybe.findFirst(
|
||||
root.subTreesAsMutable()
|
||||
.stream()
|
||||
.filter((MutableTree<T> tree) -> matchesItem(childItem, tree))
|
||||
.map(Tree::builder));
|
||||
}
|
||||
|
||||
private Boolean matchesItem(final T childItem, final MutableTree<T> tree) {
|
||||
return tree.item().map(childItem::equals).orElse(false);
|
||||
}
|
||||
|
||||
}
|
|
@ -22,11 +22,11 @@
|
|||
package net.kemitix.mon.tree;
|
||||
|
||||
import net.kemitix.mon.Functor;
|
||||
import net.kemitix.mon.maybe.Maybe;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
|
@ -62,22 +62,49 @@ public interface Tree<T> extends Functor<T, Tree<?>> {
|
|||
return new GeneralisedTree<>(item, subtrees);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TreeBuilder} starting with an empty tree.
|
||||
*
|
||||
* @param type the type
|
||||
* @param <B> the type of the tree
|
||||
*
|
||||
* @return a TreeBuilder
|
||||
*/
|
||||
public static <B> TreeBuilder<B> builder(final Class<B> type) {
|
||||
return new MutableTreeBuilder<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TreeBuilder} for the given tree.
|
||||
*
|
||||
* @param tree the tree to build upon
|
||||
* @param <B> the type of the tree
|
||||
*
|
||||
* @return a TreeBuilder
|
||||
*/
|
||||
public static <B> TreeBuilder<B> builder(final Tree<B> tree) {
|
||||
return new MutableTreeBuilder<>(MutableTree.of(tree));
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract <R> Tree<R> map(Function<T, R> f);
|
||||
|
||||
/**
|
||||
* Return the item within the node of the tree, if present.
|
||||
*
|
||||
* @return an Optional containing the item
|
||||
* @return a Maybe containing the item
|
||||
*/
|
||||
public abstract Optional<T> item();
|
||||
public abstract Maybe<T> item();
|
||||
|
||||
/**
|
||||
* Count the number of item in the tree, including subtrees.
|
||||
*
|
||||
* @return the number of items
|
||||
* @return the sum of the subtrees, plus 1 if there is an item in this node
|
||||
*/
|
||||
public abstract int count();
|
||||
public default int count() {
|
||||
return item().matchValue(x -> 1, () -> 0)
|
||||
+ subTrees().stream().mapToInt(Tree::count).sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* The subtrees of the tree.
|
||||
|
|
88
src/main/java/net/kemitix/mon/tree/TreeBuilder.java
Normal file
88
src/main/java/net/kemitix/mon/tree/TreeBuilder.java
Normal file
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Paul Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies
|
||||
* or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.kemitix.mon.tree;
|
||||
|
||||
import net.kemitix.mon.maybe.Maybe;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Mutable builder for a {@link Tree}.
|
||||
*
|
||||
* @param <T> the type of the tree to build
|
||||
*
|
||||
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||
*/
|
||||
public interface TreeBuilder<T> {
|
||||
|
||||
/**
|
||||
* Create the immutable {@link Tree}.
|
||||
*
|
||||
* @return a {@link Tree}
|
||||
*/
|
||||
public abstract Tree<T> build();
|
||||
|
||||
/**
|
||||
* Set the current {@link Tree}'s item.
|
||||
*
|
||||
* @param item the item for the current {@link Tree}
|
||||
*
|
||||
* @return the TreeBuilder
|
||||
*/
|
||||
public abstract TreeBuilder<T> item(T item);
|
||||
|
||||
/**
|
||||
* Adds the subtree to the current tree.
|
||||
*
|
||||
* @param subtree the tree to add
|
||||
*
|
||||
* @return the TreeBuilder
|
||||
*/
|
||||
public abstract TreeBuilder<T> add(Tree<T> subtree);
|
||||
|
||||
/**
|
||||
* Add the Child item as a subTree.
|
||||
*
|
||||
* @param childItem the item to add as a subtree
|
||||
* @return the TreeBuilder
|
||||
*/
|
||||
public abstract TreeBuilder<T> addChild(T childItem);
|
||||
|
||||
/**
|
||||
* Add all the child items as subTrees.
|
||||
*
|
||||
* @param children the items to add as a subtree
|
||||
* @return the TreeBuilder
|
||||
*/
|
||||
public default TreeBuilder<T> addChildren(List<T> children) {
|
||||
children.forEach(this::addChild);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TreeBuilder for the subTree of the current Tree that has the childItem.
|
||||
*
|
||||
* @param childItem the item of search the subtrees for
|
||||
* @return a Maybe containing the TreeBuilder for the subtree, or Nothing if there child item is not found
|
||||
*/
|
||||
public abstract Maybe<TreeBuilder<T>> select(T childItem);
|
||||
}
|
55
src/main/java/net/kemitix/mon/tree/TreeMapper.java
Normal file
55
src/main/java/net/kemitix/mon/tree/TreeMapper.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Paul Campbell
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies
|
||||
* or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
|
||||
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package net.kemitix.mon.tree;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Maps a list of Trees.
|
||||
*
|
||||
* @param <T> the type of the objects help in the tree
|
||||
*
|
||||
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||
*/
|
||||
interface TreeMapper<T> {
|
||||
|
||||
/**
|
||||
* Map the Trees.
|
||||
*
|
||||
* @param f the mapping function
|
||||
* @param trees the trees to map
|
||||
* @param <R> the type of the resulting Trees
|
||||
*
|
||||
* @return a List of mapped sub-trees
|
||||
*/
|
||||
public default <R> List<Tree<R>> mapTrees(
|
||||
final Function<T, R> f,
|
||||
final List<Tree<T>> trees
|
||||
) {
|
||||
return trees.stream()
|
||||
.map(subTree -> subTree.map(f))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package net.kemitix.mon;
|
||||
|
||||
import net.kemitix.mon.maybe.Maybe;
|
||||
import org.assertj.core.api.AbstractIntegerAssert;
|
||||
import org.assertj.core.api.WithAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -8,7 +9,9 @@ import java.util.Objects;
|
|||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static net.kemitix.mon.maybe.Maybe.*;
|
||||
|
@ -252,6 +255,26 @@ class MaybeTest implements WithAssertions {
|
|||
assertThat(flag).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void just_whenMatchValue_thenJustTriggers() {
|
||||
//given
|
||||
final Maybe<Integer> maybe = just(1);
|
||||
//when
|
||||
final String result = maybe.matchValue(integer -> "just", () -> "nothing");
|
||||
//then
|
||||
assertThat(result).isEqualTo("just");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nothing_whenMatchValue_thenNothingTriggers() {
|
||||
//given
|
||||
final Maybe<Integer> maybe = nothing();
|
||||
//when
|
||||
final String result = maybe.matchValue(integer -> "just", () -> "nothing");
|
||||
//then
|
||||
assertThat(result).isEqualTo("nothing");
|
||||
}
|
||||
|
||||
@Test
|
||||
void just_isJust_isTrue() {
|
||||
//given
|
||||
|
@ -311,4 +334,36 @@ class MaybeTest implements WithAssertions {
|
|||
//then
|
||||
assertThat(result.toOptional()).contains("two");
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyStream_findFirst_isNothing() {
|
||||
//given
|
||||
final Stream<Object> stream = Stream.empty();
|
||||
//when
|
||||
final Maybe<Object> result = Maybe.findFirst(stream);
|
||||
//then
|
||||
assertThat(result.isNothing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void singleItemStream_findFirst_isJustItem() {
|
||||
//given
|
||||
final String item = "item";
|
||||
final Stream<String> stream = Stream.of(item);
|
||||
//when
|
||||
final Maybe<String> result = Maybe.findFirst(stream);
|
||||
//then
|
||||
assertThat(result.toOptional()).contains(item);
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleItemStream_findFirst_isJustFirst() {
|
||||
//given
|
||||
final String first = "first";
|
||||
final Stream<String> stream = Stream.of(first, "second");
|
||||
//when
|
||||
final Maybe<String> result = Maybe.findFirst(stream);
|
||||
//then
|
||||
assertThat(result.toOptional()).contains(first);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ class GeneralisedTreeTest implements WithAssertions {
|
|||
//when
|
||||
final Tree<String> leaf = Tree.leaf(null);
|
||||
//then
|
||||
assertThat(leaf.item()).isEmpty();
|
||||
assertThat(leaf.item().isNothing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -25,7 +25,7 @@ class GeneralisedTreeTest implements WithAssertions {
|
|||
//when
|
||||
final Tree<String> leaf = Tree.leaf(item);
|
||||
//then
|
||||
assertThat(leaf.item()).contains(item);
|
||||
assertThat(leaf.item().toOptional()).contains(item);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
108
src/test/java/net/kemitix/mon/tree/MutableTreeTest.java
Normal file
108
src/test/java/net/kemitix/mon/tree/MutableTreeTest.java
Normal file
|
@ -0,0 +1,108 @@
|
|||
package net.kemitix.mon.tree;
|
||||
|
||||
import org.assertj.core.api.WithAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
class MutableTreeTest implements WithAssertions {
|
||||
|
||||
@Test
|
||||
void canCreateAnEmptyLeaf() {
|
||||
//when
|
||||
final Tree<String> leaf = MutableTree.create();
|
||||
//then
|
||||
assertThat(leaf.item().isNothing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void canCreateANonEmptyLeaf() {
|
||||
//given
|
||||
final String item = "item";
|
||||
//when
|
||||
final MutableTree<String> leaf = MutableTree.create();
|
||||
leaf.set(item);
|
||||
//then
|
||||
assertThat(leaf.item().toOptional()).contains(item);
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptyLeafHasCountZero() {
|
||||
//given
|
||||
final Tree<Object> tree = MutableTree.create();
|
||||
//when
|
||||
final int count = tree.count();
|
||||
//then
|
||||
assertThat(count).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonEmptyLeafHasCountOne() {
|
||||
//given
|
||||
final MutableTree<String> tree = MutableTree.create();
|
||||
tree.set("value");
|
||||
//when
|
||||
final int count = tree.count();
|
||||
//then
|
||||
assertThat(count).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void canCreateTreeWithSubTrees() {
|
||||
//given
|
||||
final String leafItem = "leaf";
|
||||
final MutableTree<String> leaf = MutableTree.leaf(leafItem);
|
||||
final MutableTree<String> tree = MutableTree.create();
|
||||
//when
|
||||
tree.subTrees(singletonList(leaf));
|
||||
//then
|
||||
assertThat(tree.subTrees()).containsExactly(leaf);
|
||||
}
|
||||
|
||||
@Test
|
||||
void canMapNestedTrees() {
|
||||
//given
|
||||
final UUID uid1 = UUID.randomUUID();
|
||||
final UUID uid2 = UUID.randomUUID();
|
||||
final UUID uid3 = UUID.randomUUID();
|
||||
final String sid1 = uid1.toString();
|
||||
final String sid2 = uid2.toString();
|
||||
final String sid3 = uid3.toString();
|
||||
final MutableTree<UUID> tree = MutableTree.of(
|
||||
uid1,
|
||||
asList(
|
||||
MutableTree.leaf(uid2),
|
||||
MutableTree.leaf(uid3)));
|
||||
//when
|
||||
final Tree<String> result = tree.map(UUID::toString);
|
||||
//then
|
||||
final MutableTree<String> expectedTree = MutableTree.of(
|
||||
sid1,
|
||||
asList(
|
||||
MutableTree.leaf(sid2),
|
||||
MutableTree.leaf(sid3)));
|
||||
assertThat(result).isEqualToComparingFieldByFieldRecursively(expectedTree);
|
||||
}
|
||||
|
||||
@Test
|
||||
void canCloneNonMutableTree() {
|
||||
//given
|
||||
final UUID rootItem = UUID.randomUUID();
|
||||
final UUID leafItem = UUID.randomUUID();
|
||||
final Tree<UUID> immutableTree = Tree.of(rootItem, singletonList(Tree.leaf(leafItem)));
|
||||
//when
|
||||
final MutableTree<UUID> mutableTree = MutableTree.of(immutableTree);
|
||||
//then
|
||||
assertThat(mutableTree.count()).isEqualTo(2);
|
||||
assertThat(mutableTree.item().toOptional()).contains(rootItem);
|
||||
final List<Tree<UUID>> subTrees = mutableTree.subTrees();
|
||||
assertThat(subTrees).hasSize(1);
|
||||
assertThat(subTrees.get(0).item().toOptional()).contains(leafItem);
|
||||
}
|
||||
|
||||
}
|
109
src/test/java/net/kemitix/mon/tree/TreeBuilderTest.java
Normal file
109
src/test/java/net/kemitix/mon/tree/TreeBuilderTest.java
Normal file
|
@ -0,0 +1,109 @@
|
|||
package net.kemitix.mon.tree;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kemitix.mon.maybe.Maybe;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class TreeBuilderTest {
|
||||
|
||||
@Test
|
||||
void whenEmptyBuilderBuildThenTreeIsAnEmptyLeaf() {
|
||||
//when
|
||||
final Tree<Node> result = Tree.builder(Node.class).build();
|
||||
//then
|
||||
assertThat(result.count()).isZero();
|
||||
assertThat(result.item().isNothing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenAddLeafThenTreeHasLeaf() {
|
||||
//given
|
||||
final Node node = createANode();
|
||||
//when
|
||||
final Tree<Node> result =
|
||||
Tree.builder(Node.class)
|
||||
.item(node).build();
|
||||
//then
|
||||
assertThat(result.count()).isEqualTo(1);
|
||||
assertThat(result.item().toOptional()).contains(node);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenAddSubTreeThenTreeHasSubTree() {
|
||||
//given
|
||||
final Tree<Node> subtree = MutableTree.leaf(createANode());
|
||||
//when
|
||||
final Tree<Node> result =
|
||||
Tree.builder(Node.class)
|
||||
.item(createANode())
|
||||
.add(subtree)
|
||||
.build();
|
||||
//then
|
||||
assertThat(result.count()).isEqualTo(2);
|
||||
assertThat(result.subTrees()).contains(subtree);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenAddGrandChildThenTreeHasGrandChild() {
|
||||
//given
|
||||
final Node rootNode = new Node("root");
|
||||
final Node childNode = new Node("child");
|
||||
final Node grandchildNode = new Node("grandchild");
|
||||
//when
|
||||
final TreeBuilder<Node> rootBuilder =
|
||||
Tree.builder(Node.class)
|
||||
.item(rootNode)
|
||||
.addChild(childNode);
|
||||
rootBuilder.select(childNode)
|
||||
.map(childBuilder -> childBuilder.addChild(grandchildNode));
|
||||
final Tree<Node> result = rootBuilder.build();
|
||||
//then
|
||||
assertThat(result.count()).isEqualTo(3);
|
||||
assertThat(result).isEqualToComparingFieldByFieldRecursively(
|
||||
MutableTree.of(rootNode, Collections.singleton(
|
||||
MutableTree.of(childNode, Collections.singleton(
|
||||
MutableTree.leaf(grandchildNode))))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenAddMultipleChildrenThenTreeHasAllChildren() {
|
||||
//given
|
||||
final Node rootNode = new Node("root");
|
||||
final Node child1Node = createANode();
|
||||
final Node child2Node = createANode();
|
||||
final Node child3Node = createANode();
|
||||
//when
|
||||
final Tree<Node> result =
|
||||
Tree.builder(Node.class)
|
||||
.item(rootNode)
|
||||
.addChildren(asList(child1Node, child2Node, child3Node))
|
||||
.build();
|
||||
//then
|
||||
assertThat(result.count()).isEqualTo(4);
|
||||
assertThat(result).isEqualToComparingFieldByFieldRecursively(
|
||||
MutableTree.of(rootNode, asList(
|
||||
MutableTree.leaf(child1Node),
|
||||
MutableTree.leaf(child2Node),
|
||||
MutableTree.leaf(child3Node))));
|
||||
}
|
||||
|
||||
private Node createANode() {
|
||||
return new Node(createAName());
|
||||
}
|
||||
|
||||
private String createAName() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class Node {
|
||||
private final String name;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue