Merge pull request #16 from kemitix/upgrade-kemitix-parent-to-2.0.0

Upgrade kemitix-parent to 2.0.0
This commit is contained in:
Paul Campbell 2016-09-04 22:32:52 +01:00 committed by GitHub
commit 600eb94616
9 changed files with 146 additions and 346 deletions

View file

@ -1,192 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<!--
Checkstyle configuration that checks the sun coding conventions from:
- the Java Language Specification at
http://java.sun.com/docs/books/jls/second_edition/html/index.html
- the Sun Code Conventions at http://java.sun.com/docs/codeconv/
- the Javadoc guidelines at
http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
- the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html
- some best practices
Checkstyle is very configurable. Be sure to read the documentation at
http://checkstyle.sf.net (or in your downloaded distribution).
Most Checks are configurable, be sure to consult the documentation.
To completely disable a check, just comment it out or delete it from the file.
Finally, it is worth reading the documentation.
-->
<module name="Checker">
<!--
If you set the basedir property below, then all reported file
names will be relative to the specified directory. See
http://checkstyle.sourceforge.net/5.x/config.html#Checker
<property name="basedir" value="${basedir}"/>
-->
<!-- Checks that a package-info.java file exists for each package. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage -->
<module name="JavadocPackage"/>
<!-- Checks whether files end with a new line. -->
<!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
<module name="NewlineAtEndOfFile"/>
<!-- Checks that property files contain the same keys. -->
<!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
<module name="Translation"/>
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
<module name="FileLength"/>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="FileTabCharacter"/>
<!-- Miscellaneous other checks. -->
<!-- See http://checkstyle.sf.net/config_misc.html -->
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="minimum" value="0"/>
<property name="maximum" value="0"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<!-- Checks for Headers -->
<!-- See http://checkstyle.sf.net/config_header.html -->
<!-- <module name="Header"> -->
<!-- <property name="headerFile" value="${checkstyle.header.file}"/> -->
<!-- <property name="fileExtensions" value="java"/> -->
<!-- </module> -->
<module name="TreeWalker">
<!-- Support @SuppressWarnings annotation -->
<!-- See http://checkstyle.sourceforge.net/config.html -->
<module name="SuppressWarningsHolder"/>
<module name="FileContentsHolder"/>
<!-- Checks for Javadoc comments. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
<module name="JavadocMethod">
<property name="scope" value="public"/>
</module>
<module name="JavadocType"/>
<!--<module name="JavadocVariable"/>-->
<module name="JavadocStyle"/>
<!-- Checks for Naming Conventions. -->
<!-- See http://checkstyle.sf.net/config_naming.html -->
<module name="ConstantName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<!-- Checks for imports -->
<!-- See http://checkstyle.sf.net/config_import.html -->
<module name="AvoidStarImport"/>
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
<module name="LineLength"/>
<module name="MethodLength"/>
<module name="ParameterNumber"/>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="EmptyForIteratorPad"/>
<module name="GenericWhitespace"/>
<module name="MethodParamPad"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround"/>
<!-- Modifier Checks -->
<!-- See http://checkstyle.sf.net/config_modifiers.html -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Checks for blocks. You know, those {}'s -->
<!-- See http://checkstyle.sf.net/config_blocks.html -->
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock"/>
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<module name="RightCurly"/>
<!-- Checks for common coding problems -->
<!-- See http://checkstyle.sf.net/config_coding.html -->
<module name="AvoidInlineConditionals"/>
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="HiddenField">
<property name="ignoreConstructorParameter" value="true"/>
<property name="ignoreSetter" value="true"/>
<property name="setterCanReturnItsClass" value="true"/>
</module>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<module name="MagicNumber"/>
<module name="MissingSwitchDefault"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<!-- Checks for class design -->
<!-- See http://checkstyle.sf.net/config_design.html -->
<!--<module name="DesignForExtension"/>-->
<module name="FinalClass"/>
<module name="HideUtilityClassConstructor"/>
<module name="InterfaceIsType"/>
<module name="VisibilityModifier"/>
<!-- Miscellaneous other checks. -->
<!-- See http://checkstyle.sf.net/config_misc.html -->
<module name="ArrayTypeStyle"/>
<module name="FinalParameters"/>
<module name="TodoComment"/>
<module name="UpperEll"/>
</module>
<module name="SuppressWarningsFilter"/>
<module name="SuppressionCommentFilter"/>
</module>

View file

@ -11,7 +11,7 @@
<parent>
<groupId>net.kemitix</groupId>
<artifactId>kemitix-parent</artifactId>
<version>0.6.0</version>
<version>2.0.0</version>
</parent>
<properties>
@ -50,7 +50,6 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>

View file

@ -1,5 +1,7 @@
package net.kemitix.node;
import lombok.NonNull;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@ -9,9 +11,9 @@ import java.util.Set;
* An abstract node item, providing default implementations for most read-only
* operations.
*
* @param <T> the type of data stored in each node
* @author Paul Campbell
*
* @author pcampbell
* @param <T> the type of data stored in each node
*/
abstract class AbstractNodeItem<T> implements Node<T> {
@ -65,10 +67,7 @@ abstract class AbstractNodeItem<T> implements Node<T> {
* @return an {@link Optional} containing the child node if found
*/
@Override
public Optional<Node<T>> findChild(final T child) {
if (child == null) {
throw new NullPointerException("child");
}
public Optional<Node<T>> findChild(@NonNull final T child) {
return children.stream().filter(node -> {
final Optional<T> d = node.getData();
return d.isPresent() && d.get().equals(child);
@ -102,27 +101,25 @@ abstract class AbstractNodeItem<T> implements Node<T> {
* @return the child or null
*/
@Override
public Optional<Node<T>> findInPath(final List<T> path) {
if (path == null) {
throw new NullPointerException("path");
public Optional<Node<T>> findInPath(@NonNull final List<T> path) {
if (path.isEmpty()) {
return Optional.empty();
}
if (path.size() > 0) {
Optional<Node<T>> found = findChild(path.get(0));
if (found.isPresent()) {
if (path.size() > 1) {
return found.get().findInPath(path.subList(1, path.size()));
}
return found;
Node<T> current = this;
for (T item : path) {
final Optional<Node<T>> child = current.findChild(item);
if (child.isPresent()) {
current = child.get();
} else {
current = null;
break;
}
}
return Optional.empty();
return Optional.ofNullable(current);
}
@Override
public Optional<Node<T>> findChildByName(final String named) {
if (named == null) {
throw new NullPointerException("name");
}
public Optional<Node<T>> findChildByName(@NonNull final String named) {
return children.stream()
.filter(n -> n.getName().equals(named))
.findAny();
@ -139,17 +136,18 @@ abstract class AbstractNodeItem<T> implements Node<T> {
final StringBuilder sb = new StringBuilder();
final String unnamed = "(unnamed)";
if (isNamed()) {
sb.append(String.format("[%1$" + (depth + name.length()) + "s]\n",
name));
sb.append(formatByDepth(name, depth));
} else if (!children.isEmpty()) {
sb.append(
String.format("[%1$" + (depth + unnamed.length()) + "s]\n",
unnamed));
sb.append(formatByDepth(unnamed, depth));
}
getChildren().forEach(c -> sb.append(c.drawTree(depth + 1)));
return sb.toString();
}
private String formatByDepth(final String value, final int depth) {
return String.format("[%1$" + (depth + value.length()) + "s]\n", value);
}
@Override
public boolean isNamed() {
return name != null && name.length() > 0;

View file

@ -11,9 +11,9 @@ import java.util.Set;
* getData()} they could then modify the original data within the node. This
* wouldn't affect the integrity of the node tree structure, however.</p>
*
* @param <T> the type of data stored in each node
* @author Paul Campbell
*
* @author pcampbell
* @param <T> the type of data stored in each node
*/
final class ImmutableNodeItem<T> extends AbstractNodeItem<T> {

View file

@ -7,9 +7,9 @@ import java.util.Set;
/**
* An interface for tree node items.
*
* @param <T> the type of data held in each node
* @author Paul Campbell
*
* @author pcampbell
* @param <T> the type of data held in each node
*/
public interface Node<T> {
@ -113,6 +113,9 @@ public interface Node<T> {
* @param child the child's data to search or create with
*
* @return the found or created child node
*
* @deprecated use node.findChild(child).orElseGet(() ->
* node.createChild(child));
*/
@Deprecated
Node<T> findOrCreateChild(T child);

View file

@ -1,18 +1,20 @@
package net.kemitix.node;
import lombok.NonNull;
import lombok.val;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
/**
* Represents a tree of nodes.
*
* @param <T> the type of data stored in each node
* @author Paul Campbell
*
* @author pcampbell
* @param <T> the type of data stored in each node
*/
class NodeItem<T> implements Node<T> {
@ -20,8 +22,6 @@ class NodeItem<T> implements Node<T> {
private final Set<Node<T>> children = new HashSet<>();
private Function<Node<T>, String> nameSupplier;
private Node<T> parent;
private String name;
@ -44,7 +44,6 @@ class NodeItem<T> implements Node<T> {
*/
NodeItem(final T data) {
this.data = data;
this.nameSupplier = (n) -> null;
}
/**
@ -71,24 +70,8 @@ class NodeItem<T> implements Node<T> {
setParent(parent);
}
private String generateName() {
return getNameSupplier().apply(this);
}
private Function<Node<T>, String> getNameSupplier() {
if (nameSupplier != null) {
return nameSupplier;
}
// no test for parent as root nodes will always have a default name
// supplier
return ((NodeItem<T>) parent).getNameSupplier();
}
@Override
public String getName() {
if (name == null) {
return generateName();
}
return name;
}
@ -128,32 +111,35 @@ class NodeItem<T> implements Node<T> {
* @param child the node to add
*/
@Override
public void addChild(final Node<T> child) {
if (child == null) {
throw new NullPointerException("child");
}
if (this.equals(child) || isDescendantOf(child)) {
throw new NodeException("Child is an ancestor");
}
if (child.isNamed()) {
final Optional<Node<T>> existingChild = findChildByName(
child.getName());
if (existingChild.isPresent() && existingChild.get() != child) {
throw new NodeException(
"Node with that name already exists here");
}
}
public void addChild(@NonNull final Node<T> child) {
verifyChildIsNotAnAncestor(child);
verifyChildWithSameNameDoesNotAlreadyExist(child);
children.add(child);
// update the child's parent if they don't have one or it is not this
Optional<Node<T>> childParent = child.getParent();
boolean isOrphan = !childParent.isPresent();
boolean hasDifferentParent = !isOrphan && !childParent.get()
.equals(this);
if (isOrphan || hasDifferentParent) {
val childParent = child.getParent();
if (!childParent.isPresent() || !childParent.get().equals(this)) {
child.setParent(this);
}
}
private void verifyChildWithSameNameDoesNotAlreadyExist(
final @NonNull Node<T> child) {
if (child.isNamed()) {
findChildByName(child.getName())
.filter(existingChild -> existingChild != child)
.ifPresent(existingChild -> {
throw new NodeException(
"Node with that name already exists here");
});
}
}
private void verifyChildIsNotAnAncestor(final @NonNull Node<T> child) {
if (this.equals(child) || isDescendantOf(child)) {
throw new NodeException("Child is an ancestor");
}
}
/**
* Creates a new node and adds it as a child of the current node.
*
@ -162,10 +148,7 @@ class NodeItem<T> implements Node<T> {
* @return the new child node
*/
@Override
public Node<T> createChild(final T child) {
if (child == null) {
throw new NullPointerException("child");
}
public Node<T> createChild(@NonNull final T child) {
return new NodeItem<>(child, this);
}
@ -184,10 +167,7 @@ class NodeItem<T> implements Node<T> {
* @param descendants the line of descendants from the current node
*/
@Override
public void createDescendantLine(final List<T> descendants) {
if (descendants == null) {
throw new NullPointerException("descendants");
}
public void createDescendantLine(@NonNull final List<T> descendants) {
if (!descendants.isEmpty()) {
findOrCreateChild(descendants.get(0)).createDescendantLine(
descendants.subList(1, descendants.size()));
@ -201,13 +181,13 @@ class NodeItem<T> implements Node<T> {
* @param child the child's data to search or create with
*
* @return the found or created child node
*
* @deprecated use node.findChild(child).orElseGet(() -> node.createChild
* (child));
*/
@Override
@Deprecated
public Node<T> findOrCreateChild(final T child) {
if (child == null) {
throw new NullPointerException("child");
}
public Node<T> findOrCreateChild(@NonNull final T child) {
return findChild(child).orElseGet(() -> createChild(child));
}
@ -219,10 +199,7 @@ class NodeItem<T> implements Node<T> {
* @return an {@link Optional} containing the child node if found
*/
@Override
public Optional<Node<T>> findChild(final T child) {
if (child == null) {
throw new NullPointerException("child");
}
public Optional<Node<T>> findChild(@NonNull final T child) {
return children.stream().filter(node -> {
final Optional<T> d = node.getData();
return d.isPresent() && d.get().equals(child);
@ -257,10 +234,7 @@ class NodeItem<T> implements Node<T> {
* @param parent the new parent node
*/
@Override
public final void setParent(final Node<T> parent) {
if (parent == null) {
throw new NullPointerException("parent");
}
public final void setParent(@NonNull final Node<T> parent) {
if (this.equals(parent) || parent.isDescendantOf(this)) {
throw new NodeException("Parent is a descendant");
}
@ -279,64 +253,68 @@ class NodeItem<T> implements Node<T> {
* @return the child or null
*/
@Override
public Optional<Node<T>> findInPath(final List<T> path) {
if (path == null) {
throw new NullPointerException("path");
public Optional<Node<T>> findInPath(@NonNull final List<T> path) {
if (path.isEmpty()) {
return Optional.empty();
}
if (path.size() > 0) {
Optional<Node<T>> found = findChild(path.get(0));
if (found.isPresent()) {
if (path.size() > 1) {
return found.get().findInPath(path.subList(1, path.size()));
}
return found;
Node<T> current = this;
for (T item : path) {
final Optional<Node<T>> child = current.findChild(item);
if (child.isPresent()) {
current = child.get();
} else {
current = null;
break;
}
}
return Optional.empty();
return Optional.ofNullable(current);
}
@Override
public void insertInPath(final Node<T> nodeItem, final String... path) {
if (path.length == 0) {
if (!nodeItem.isNamed()) { // nothing to conflict with
addChild(nodeItem);
return;
}
String nodeName = nodeItem.getName();
final Optional<Node<T>> childNamed = findChildByName(nodeName);
if (!childNamed.isPresent()) { // nothing with the same name exists
addChild(nodeItem);
return;
}
// we have an existing node with the same name
final Node<T> existing = childNamed.get();
if (!existing.isEmpty()) {
throw new NodeException("A non-empty node named '" + nodeName
+ "' already exists here");
} else {
nodeItem.getData().ifPresent(existing::setData);
}
return;
}
String item = path[0];
final Optional<Node<T>> childNamed = findChildByName(item);
Node<T> child;
if (!childNamed.isPresent()) {
child = new NodeItem<>(null, item, this);
insertChild(nodeItem);
} else {
child = childNamed.get();
val item = path[0];
findChildByName(item)
.orElseGet(() -> new NodeItem<>(null, item, this))
.insertInPath(nodeItem,
Arrays.copyOfRange(path, 1, path.length));
}
}
private void insertChild(final Node<T> nodeItem) {
if (nodeItem.isNamed()) {
insertNamedChild(nodeItem);
} else {
// nothing to conflict with
addChild(nodeItem);
}
}
private void insertNamedChild(final Node<T> nodeItem) {
val childByName = findChildByName(nodeItem.getName());
if (childByName.isPresent()) {
// we have an existing node with the same name
val existing = childByName.get();
if (existing.isEmpty()) {
// place any data in the new node into the existing empty node
nodeItem.getData().ifPresent(existing::setData);
} else {
throw new NodeException("A non-empty node named '"
+ nodeItem.getName() + "' already exists here");
}
} else {
// nothing with the same name exists
addChild(nodeItem);
}
child.insertInPath(nodeItem, Arrays.copyOfRange(path, 1, path.length));
}
@Override
public Optional<Node<T>> findChildByName(final String named) {
if (named == null) {
throw new NullPointerException("name");
}
public Optional<Node<T>> findChildByName(@NonNull final String named) {
return children.stream()
.filter((Node<T> t) -> t.getName().equals(named))
.findAny();
.filter((Node<T> t) -> t.getName().equals(named))
.findAny();
}
@Override
@ -353,17 +331,18 @@ class NodeItem<T> implements Node<T> {
final StringBuilder sb = new StringBuilder();
final String unnamed = "(unnamed)";
if (isNamed()) {
sb.append(String.format("[%1$" + (depth + name.length()) + "s]\n",
name));
sb.append(formatByDepth(name, depth));
} else if (!children.isEmpty()) {
sb.append(
String.format("[%1$" + (depth + unnamed.length()) + "s]\n",
unnamed));
sb.append(formatByDepth(unnamed, depth));
}
getChildren().stream().forEach(c -> sb.append(c.drawTree(depth + 1)));
getChildren().forEach(c -> sb.append(c.drawTree(depth + 1)));
return sb.toString();
}
private String formatByDepth(final String value, final int depth) {
return String.format("[%1$" + (depth + value.length()) + "s]\n", value);
}
@Override
public boolean isNamed() {
String currentName = getName();
@ -381,14 +360,8 @@ class NodeItem<T> implements Node<T> {
public void removeParent() {
if (parent != null) {
Node<T> oldParent = parent;
Function<Node<T>, String> supplier = getNameSupplier();
parent = null;
oldParent.removeChild(this);
if (this.nameSupplier == null) {
// this is now a root node, so must provide a default name
// supplier
this.nameSupplier = supplier;
}
}
}

View file

@ -4,14 +4,15 @@ import lombok.val;
import org.assertj.core.api.SoftAssertions;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.ExpectedException;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link ImmutableNodeItem}.
*
@ -141,6 +142,7 @@ public class ImmutableNodeItemTest {
* Test that we can walk a tree to the target node.
*/
@Test
@Category(NodeFindInPathTestsCategory.class)
public void shouldWalkTreeToNode() {
//given
val root = Nodes.unnamedRoot("root");
@ -160,6 +162,7 @@ public class ImmutableNodeItemTest {
* doesn't exist.
*/
@Test
@Category(NodeFindInPathTestsCategory.class)
public void shouldNotFindNonExistentChildNode() {
//given
val root = Nodes.unnamedRoot("root");
@ -176,6 +179,7 @@ public class ImmutableNodeItemTest {
* Test that when we pass null we get an exception.
*/
@Test
@Category(NodeFindInPathTestsCategory.class)
public void shouldThrowNEWhenWalkTreeNull() {
//given
immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("subject"));
@ -190,6 +194,7 @@ public class ImmutableNodeItemTest {
* a result.
*/
@Test
@Category(NodeFindInPathTestsCategory.class)
public void shouldReturnEmptyForEmptyWalkTreePath() {
//given
immutableNode = Nodes.asImmutable(Nodes.unnamedRoot("subject"));

View file

@ -0,0 +1,9 @@
package net.kemitix.node;
/**
* Category marker for tests relating to implementations of Node.findInPath(...).
*
* @author Paul Campbell
*/
public interface NodeFindInPathTestsCategory {
}

View file

@ -4,14 +4,15 @@ import lombok.val;
import org.assertj.core.api.SoftAssertions;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.ExpectedException;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link NodeItem}.
*
@ -309,6 +310,7 @@ public class NodeItemTest {
* Test that we can walk a tree to the target node.
*/
@Test
@Category(NodeFindInPathTestsCategory.class)
public void shouldWalkTreeToNode() {
//given
val grandparent = "grandparent";
@ -334,6 +336,7 @@ public class NodeItemTest {
* doesn't exist.
*/
@Test
@Category(NodeFindInPathTestsCategory.class)
public void shouldNotFindNonExistentChildNode() {
//given
val parent = "parent";
@ -352,6 +355,7 @@ public class NodeItemTest {
* Test that when we pass null we get an exception.
*/
@Test
@Category(NodeFindInPathTestsCategory.class)
public void shouldThrowNEWhenWalkTreeNull() {
//given
node = Nodes.unnamedRoot("subject");
@ -366,6 +370,7 @@ public class NodeItemTest {
* a result.
*/
@Test
@Category(NodeFindInPathTestsCategory.class)
public void shouldReturnEmptyForEmptyWalkTreePath() {
//given
node = Nodes.unnamedRoot("subject");