[tree] Add a Generalised Tree
This commit is contained in:
parent
073df36e56
commit
f5c10e668d
5 changed files with 394 additions and 17 deletions
108
README.org
108
README.org
|
@ -2,26 +2,32 @@
|
|||
|
||||
** TypeAlias, Maybe and Result for Java.
|
||||
|
||||
[[https://oss.sonatype.org/content/repositories/releases/net/kemitix/mon][file:https://img.shields.io/nexus/r/https/oss.sonatype.org/net.kemitix/mon.svg?style=for-the-badge]]
|
||||
[[https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.kemitix%22%20AND%20a%3A%22mon%22][file:https://img.shields.io/maven-central/v/net.kemitix/mon.svg?style=for-the-badge]]
|
||||
[[https://oss.sonatype.org/content/repositories/releases/net/kemitix/mon][file:https://img.shields.io/nexus/r/https/oss.sonatype.org/net.kemitix/mon.svg?style=for-the-badge]]
|
||||
[[https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.kemitix%22%20AND%20a%3A%22mon%22][file:https://img.shields.io/maven-central/v/net.kemitix/mon.svg?style=for-the-badge]]
|
||||
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://img.shields.io/sonar/https/sonarcloud.io/net.kemitix%3Amon/coverage.svg?style=for-the-badge#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://img.shields.io/sonar/https/sonarcloud.io/net.kemitix%3Amon/tech_debt.svg?style=for-the-badge#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=sqale_rating#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=alert_status#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=reliability_rating#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=security_rating#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=sqale_index#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=vulnerabilities#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=bugs#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=code_smells#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=ncloc#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://img.shields.io/sonar/https/sonarcloud.io/net.kemitix%3Amon/coverage.svg?style=for-the-badge#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://img.shields.io/sonar/https/sonarcloud.io/net.kemitix%3Amon/tech_debt.svg?style=for-the-badge#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=sqale_rating#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=alert_status#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=reliability_rating#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=security_rating#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=sqale_index#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=vulnerabilities#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=bugs#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=code_smells#.svg]]
|
||||
[[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=ncloc#.svg]]
|
||||
|
||||
[[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]]
|
||||
[[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]]
|
||||
|
||||
- [Maven Usage]
|
||||
- [TypeAlias]
|
||||
- [Maybe]
|
||||
- [Result]
|
||||
- [Tree]
|
||||
- [Either]
|
||||
|
||||
** Maven
|
||||
** Maven Usage
|
||||
|
||||
#+BEGIN_SRC xml
|
||||
<dependency>
|
||||
|
@ -798,7 +804,6 @@
|
|||
.thenWith(v -> () -> {throw new IOException();});
|
||||
#+END_SRC
|
||||
|
||||
|
||||
**** =Result<Maybe<T>> maybe(Predicate<T> predicate)=
|
||||
|
||||
Wraps the value within the Result in a Maybe, either a Just if the
|
||||
|
@ -851,6 +856,75 @@
|
|||
#+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
|
||||
|
||||
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