[tree] Add a Generalised Tree
This commit is contained in:
parent
073df36e56
commit
f5c10e668d
5 changed files with 394 additions and 17 deletions
78
README.org
78
README.org
|
@ -20,8 +20,14 @@
|
||||||
[[https://app.codacy.com/project/kemitix/mon/dashboard][file:https://img.shields.io/codacy/grade/d57096b0639d496aba9a7e43e7cf5b4c.svg?style=for-the-badge]]
|
[[https://app.codacy.com/project/kemitix/mon/dashboard][file:https://img.shields.io/codacy/grade/d57096b0639d496aba9a7e43e7cf5b4c.svg?style=for-the-badge]]
|
||||||
[[http://i.jpeek.org/net.kemitix/mon/index.html][file:http://i.jpeek.org/net.kemitix/mon/badge.svg]]
|
[[http://i.jpeek.org/net.kemitix/mon/index.html][file:http://i.jpeek.org/net.kemitix/mon/badge.svg]]
|
||||||
|
|
||||||
|
- [Maven Usage]
|
||||||
|
- [TypeAlias]
|
||||||
|
- [Maybe]
|
||||||
|
- [Result]
|
||||||
|
- [Tree]
|
||||||
|
- [Either]
|
||||||
|
|
||||||
** Maven
|
** Maven Usage
|
||||||
|
|
||||||
#+BEGIN_SRC xml
|
#+BEGIN_SRC xml
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -798,7 +804,6 @@
|
||||||
.thenWith(v -> () -> {throw new IOException();});
|
.thenWith(v -> () -> {throw new IOException();});
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
**** =Result<Maybe<T>> maybe(Predicate<T> predicate)=
|
**** =Result<Maybe<T>> maybe(Predicate<T> predicate)=
|
||||||
|
|
||||||
Wraps the value within the Result in a Maybe, either a Just if the
|
Wraps the value within the Result in a Maybe, either a Just if the
|
||||||
|
@ -851,6 +856,75 @@
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
|
** Tree
|
||||||
|
|
||||||
|
A Generalised tree, where each node may or may not have an item, and may have
|
||||||
|
any number of sub-trees. Leaf nodes are Trees with zero sub-trees.
|
||||||
|
|
||||||
|
*** Static Constructors
|
||||||
|
|
||||||
|
**** =static <R> Tree<R> leaf(R item)=
|
||||||
|
|
||||||
|
Create a leaf containing the item. The leaf has no sub-trees.
|
||||||
|
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
final Tree<String> tree = Tree.leaf("item");
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
|
**** =static<R> Tree<R> of(R item, Collection<Tree<R>> subtrees)=
|
||||||
|
|
||||||
|
Create a tree containing the item and sub-trees.
|
||||||
|
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
final Tree<String> tree = Tree.of("item", Collections.singletonList(Tree.leaf("leaf"));
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
|
*** Instance Methods
|
||||||
|
|
||||||
|
**** =<R> Tree<R> map(Function<T, R> f)=
|
||||||
|
|
||||||
|
Applies the function to the item within the Tree and to all sub-trees,
|
||||||
|
returning a new Tree.
|
||||||
|
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
final Tree<UUID> tree = ...;
|
||||||
|
final Tree<String> result = tree.map(UUID::toString);
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
|
**** =Optional<T> item()=
|
||||||
|
|
||||||
|
Returns the contents of the Tree node within an Optional.
|
||||||
|
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
final Tree<Item> tree = ...;
|
||||||
|
final Optional<Item> result = tree.item();
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
|
**** =int count()=
|
||||||
|
|
||||||
|
Returns the total number of items in the tree, including sub-trees. Null
|
||||||
|
items don't count.
|
||||||
|
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
final Tree<Item> tree = ...;
|
||||||
|
final int result = tree.count();
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
|
**** =List<Tree<T> subTrees()=
|
||||||
|
|
||||||
|
Returns a list of sub-trees within the tree.
|
||||||
|
|
||||||
|
#+BEGIN_SRC java
|
||||||
|
final Tree<Item> tree = ...;
|
||||||
|
final List<Tree<Item>> result = tree.subTrees();
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
|
||||||
** Either
|
** Either
|
||||||
|
|
||||||
Allows handling a value that can be one of two types, a left value/type or a
|
Allows handling a value that can be one of two types, a left value/type or a
|
||||||
|
|
100
src/main/java/net/kemitix/mon/tree/GeneralisedTree.java
Normal file
100
src/main/java/net/kemitix/mon/tree/GeneralisedTree.java
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/**
|
||||||
|
* 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 java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic tree of trees and objects.
|
||||||
|
*
|
||||||
|
* <p>Each node may contain between 0 and n objects.</p>
|
||||||
|
*
|
||||||
|
* @param <T> the type of the objects help in the tree
|
||||||
|
*
|
||||||
|
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode
|
||||||
|
class GeneralisedTree<T> implements Tree<T> {
|
||||||
|
|
||||||
|
private final T item;
|
||||||
|
private final List<Tree<T>> subTrees;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new tree.
|
||||||
|
*
|
||||||
|
* @param item the item of this node
|
||||||
|
* @param subTrees the sub-trees under this node
|
||||||
|
*/
|
||||||
|
GeneralisedTree(final T item, final Collection<Tree<T>> subTrees) {
|
||||||
|
this.item = item;
|
||||||
|
this.subTrees = Collections.unmodifiableList(new ArrayList<>(subTrees));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the tree using the function onto a new tree.
|
||||||
|
*
|
||||||
|
* @param f the function to apply
|
||||||
|
* @param <R> the type of object held in the resulting tree
|
||||||
|
* @return a tree
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <R> Tree<R> map(final Function<T, R> f) {
|
||||||
|
return Tree.of(f.apply(item), mapSubTrees(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of subtrees.
|
||||||
|
*
|
||||||
|
* @return a List of trees
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Tree<T>> subTrees() {
|
||||||
|
return subTrees;
|
||||||
|
}
|
||||||
|
}
|
88
src/main/java/net/kemitix/mon/tree/Tree.java
Normal file
88
src/main/java/net/kemitix/mon/tree/Tree.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.Functor;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tree of objects.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the objects help in the tree
|
||||||
|
*
|
||||||
|
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||||
|
*/
|
||||||
|
public interface Tree<T> extends Functor<T, Tree<?>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new generalised tree to hold object of a type.
|
||||||
|
*
|
||||||
|
* @param item the item for the leaf node
|
||||||
|
* @param <R> the type of the object to be held in the tree
|
||||||
|
*
|
||||||
|
* @return a empty generalised tree
|
||||||
|
*/
|
||||||
|
public static <R> Tree<R> leaf(final R item) {
|
||||||
|
return new GeneralisedTree<>(item, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new generalised tree to hold the single item.
|
||||||
|
*
|
||||||
|
* @param item the item for the tree node
|
||||||
|
* @param subtrees the subtrees of the tree node
|
||||||
|
* @param <R> the type of the item
|
||||||
|
* @return a leaf node of a generalised tree
|
||||||
|
*/
|
||||||
|
public static <R> Tree<R> of(final R item, final Collection<Tree<R>> subtrees) {
|
||||||
|
return new GeneralisedTree<>(item, subtrees);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
*/
|
||||||
|
public abstract Optional<T> item();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the number of item in the tree, including subtrees.
|
||||||
|
*
|
||||||
|
* @return the number of items
|
||||||
|
*/
|
||||||
|
public abstract int count();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The subtrees of the tree.
|
||||||
|
*
|
||||||
|
* @return a list of Trees
|
||||||
|
*/
|
||||||
|
public abstract List<Tree<T>> subTrees();
|
||||||
|
}
|
28
src/main/java/net/kemitix/mon/tree/package-info.java
Normal file
28
src/main/java/net/kemitix/mon/tree/package-info.java
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tree data structures.
|
||||||
|
*
|
||||||
|
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.kemitix.mon.tree;
|
87
src/test/java/net/kemitix/mon/tree/GeneralisedTreeTest.java
Normal file
87
src/test/java/net/kemitix/mon/tree/GeneralisedTreeTest.java
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package net.kemitix.mon.tree;
|
||||||
|
|
||||||
|
import org.assertj.core.api.WithAssertions;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
|
||||||
|
public class GeneralisedTreeTest implements WithAssertions {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canCreateAnEmptyLeaf() {
|
||||||
|
//when
|
||||||
|
final Tree<String> leaf = Tree.leaf(null);
|
||||||
|
//then
|
||||||
|
assertThat(leaf.item()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canCreateANonEmptyLeaf() {
|
||||||
|
//given
|
||||||
|
final String item = "item";
|
||||||
|
//when
|
||||||
|
final Tree<String> leaf = Tree.leaf(item);
|
||||||
|
//then
|
||||||
|
assertThat(leaf.item()).contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void emptyLeafHasCountZero() {
|
||||||
|
//given
|
||||||
|
final Tree<Object> tree = Tree.leaf(null);
|
||||||
|
//when
|
||||||
|
final int count = tree.count();
|
||||||
|
//then
|
||||||
|
assertThat(count).isZero();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nonEmptyLeafHasCountOne() {
|
||||||
|
//given
|
||||||
|
final Tree<String> tree = Tree.leaf("value");
|
||||||
|
//when
|
||||||
|
final int count = tree.count();
|
||||||
|
//then
|
||||||
|
assertThat(count).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canCreateTreeWithSubTrees() {
|
||||||
|
//given
|
||||||
|
final String treeItem = "tree";
|
||||||
|
final String leafItem = "leaf";
|
||||||
|
//when
|
||||||
|
final Tree<String> tree = Tree.of(treeItem, singletonList(Tree.leaf(leafItem)));
|
||||||
|
//then
|
||||||
|
assertThat(tree.subTrees()).containsExactly(Tree.leaf(leafItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public 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 Tree<UUID> tree = Tree.of(
|
||||||
|
uid1,
|
||||||
|
asList(
|
||||||
|
Tree.leaf(uid2),
|
||||||
|
Tree.leaf(uid3)));
|
||||||
|
//when
|
||||||
|
final Tree<String> result = tree.map(UUID::toString);
|
||||||
|
//then
|
||||||
|
assertThat(result).isEqualTo(Tree.of(
|
||||||
|
sid1,
|
||||||
|
asList(
|
||||||
|
Tree.leaf(sid2),
|
||||||
|
Tree.leaf(sid3)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue