javaslang.collection.RoseTree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javaslang Show documentation
Show all versions of javaslang Show documentation
Javaslang is a Java standard library extension built for Java 8 and above.
/** / \____ _ ______ _____ / \____ ____ _____
* / \__ \/ \ / \__ \ / __// \__ \ / \/ __ \ Javaslang
* _/ // _\ \ \/ / _\ \\_ \/ // _\ \ /\ \__/ / Copyright 2014-2015 Daniel Dietrich
* /___/ \_____/\____/\_____/____/\___\_____/_/ \_/____/ Licensed under the Apache License, Version 2.0
*/
package javaslang.collection;
import javaslang.Tuple;
import javaslang.Tuple.*;
import java.io.*;
import java.util.Objects;
import java.util.function.Function;
/**
* A rose tree implementation, i.e. a tree with an arbitrary number of children, where each node keeps a value.
* See Wikipedia: Rose tree.
*
* @param the type of a Node's value.
*/
// TODO: Implement ValueObject
public interface RoseTree extends Tree {
@SafeVarargs
static NonNil of(T value, NonNil... children) {
Objects.requireNonNull(children, "children is null");
if (children.length == 0) {
return new Leaf<>(value);
} else {
return new Branch<>(value, List.of(children));
}
}
@SafeVarargs
static Branch branch(T value, NonNil child1, NonNil... children) {
Objects.requireNonNull(children, "child1 is null");
Objects.requireNonNull(children, "children is null");
return new Branch<>(value, List.of(children).prepend(child1));
}
static Leaf leaf(T value) {
return new Leaf<>(value);
}
static Nil nil() {
return Nil.instance();
}
@Override
List> getChildren();
@SuppressWarnings("unchecked")
@Override
default RoseTree map(Function super T, ? extends U> mapper) {
if (isEmpty()) {
return Nil.instance();
} else if (isLeaf()) {
return new Leaf<>(mapper.apply(getValue()));
} else {
final U value = mapper.apply(getValue());
final List children = getChildren().map(tree -> tree.map(mapper));
/*
* DEV-NOTE: The constructor of Branch and the factory method RoseTree.of
* expect NonNil children. With the implementation of map this implies that
* the result of the method getChildren().map is of type List.
*/
return new Branch<>(value, (List>) children);
}
}
/**
* Implementors of this tagging interface indicate that they are not Nil.
*
* @param Component type of the rose tree.
*/
static interface NonNil extends RoseTree {
}
static final class Leaf extends AbstractRoseTree implements NonNil {
private static final long serialVersionUID = -6301673452872179894L;
private final T value;
public Leaf(T value) {
this.value = value;
}
@Override
public T getValue() {
return value;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean isLeaf() {
return true;
}
@Override
public List> getChildren() {
return List.nil();
}
@Override
public Tuple1 unapply() {
return Tuple.of(value);
}
}
static final class Branch extends AbstractRoseTree implements NonNil {
private static final long serialVersionUID = -1368274890360703478L;
private final List> children;
private final T value;
public Branch(T value, List> children) {
Objects.requireNonNull(children, "children is null");
if (children.isEmpty()) {
throw new IllegalArgumentException("no children");
}
this.children = children;
this.value = value;
}
@Override
public T getValue() {
return value;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean isLeaf() {
return false;
}
@Override
public List> getChildren() {
return children;
}
@Override
public Tuple2>> unapply() {
return Tuple.of(value, children);
}
// -- Serializable implementation
/**
*
* {@code writeReplace} method for the serialization proxy pattern.
*
* The presence of this method causes the serialization system to emit a SerializationProxy instance instead of
* an instance of the enclosing class.
*
* @return A SerialiationProxy for this enclosing class.
*/
private Object writeReplace() {
return new SerializationProxy<>(this);
}
/**
*
* {@code readObject} method for the serialization proxy pattern.
*
* Guarantees that the serialization system will never generate a serialized instance of the enclosing class.
*
* @param stream An object serialization stream.
* @throws java.io.InvalidObjectException This method will throw with the message "Proxy required".
*/
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
/**
* A serialization proxy which, in this context, is used to deserialize immutable nodes with final
* instance fields.
*
* @param The component type of the underlying tree.
*/
// DEV NOTE: The serialization proxy pattern is not compatible with non-final, i.e. extendable,
// classes. Also, it may not be compatible with circular object graphs.
private static final class SerializationProxy implements Serializable {
private static final long serialVersionUID = -1169723642575166947L;
// the instance to be serialized/deserialized
private transient Branch branch;
/**
* Constructor for the case of serialization, called by {@link Branch#writeReplace()}.
*
* The constructor of a SerializationProxy takes an argument that concisely represents the logical state of
* an instance of the enclosing class.
*
* @param branch a Branch
*/
SerializationProxy(Branch branch) {
this.branch = branch;
}
/**
* Write an object to a serialization stream.
*
* @param s An object serialization stream.
* @throws java.io.IOException If an error occurs writing to the stream.
*/
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeObject(branch.value);
s.writeObject(branch.children);
}
/**
* Read an object from a deserialization stream.
*
* @param s An object deserialization stream.
* @throws ClassNotFoundException If the object's class read from the stream cannot be found.
* @throws IOException If an error occurs reading from the stream.
*/
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
s.defaultReadObject();
final T value = (T) s.readObject();
final List> children = (List>) s.readObject();
branch = new Branch<>(value, children);
}
/**
*
* {@code readResolve} method for the serialization proxy pattern.
*
* Returns a logically equivalent instance of the enclosing class. The presence of this method causes the
* serialization system to translate the serialization proxy back into an instance of the enclosing class
* upon deserialization.
*
* @return A deserialized instance of the enclosing class.
*/
private Object readResolve() {
return branch;
}
}
}
static final class Nil extends AbstractRoseTree {
private static final long serialVersionUID = 4966576338736993154L;
private static final Nil> INSTANCE = new Nil<>();
// hidden
private Nil() {
}
public static Nil instance() {
@SuppressWarnings("unchecked")
final Nil instance = (Nil) INSTANCE;
return instance;
}
@Override
public T getValue() {
throw new UnsupportedOperationException("getValue of Nil");
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public boolean isLeaf() {
return false;
}
@Override
public List> getChildren() {
return List.nil();
}
@Override
public Tuple0 unapply() {
return Tuple0.instance();
}
// -- Serializable implementation
/**
* Instance control for object serialization.
*
* @return The singleton instance of Nil.
* @see java.io.Serializable
*/
private Object readResolve() {
return INSTANCE;
}
}
static abstract class AbstractRoseTree implements RoseTree {
private static final long serialVersionUID = -8001420483694263100L;
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof RoseTree)) {
return false;
} else {
final RoseTree that = (RoseTree) o;
return (this.isEmpty() && that.isEmpty()) || (!this.isEmpty() && !that.isEmpty()
&& Objects.equals(this.getValue(), that.getValue())
&& this.getChildren().equals(that.getChildren()));
}
}
@Override
public int hashCode() {
if (isEmpty()) {
return 1;
} else {
// need cast because of jdk 1.8.0_25 compiler error
//noinspection RedundantCast
return (int) getChildren().map(Objects::hashCode).foldLeft(31 + Objects.hashCode(getValue()), (i, j) -> i * 31 + j);
}
}
@Override
public String toString() {
return RoseTree.class.getSimpleName() + toLispString();
}
}
}