All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javaslang.collection.RoseTree Maven / Gradle / Ivy

There is a newer version: 2.0.0-RC4
Show newest version
/**    / \____  _    ______   _____ / \____   ____  _____
 *    /  \__  \/ \  / \__  \ /  __//  \__  \ /    \/ __  \   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 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(); } } }