TreeNode.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-7.1.0).
003  * Copyright (c) 2007-2022 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019  */
020 package io.jenetics.ext.util;
021 
022 import static java.lang.String.format;
023 import static java.util.Objects.requireNonNull;
024 
025 import java.io.IOException;
026 import java.io.InvalidObjectException;
027 import java.io.ObjectInput;
028 import java.io.ObjectInputStream;
029 import java.io.ObjectOutput;
030 import java.io.Serial;
031 import java.io.Serializable;
032 import java.util.ArrayList;
033 import java.util.Collections;
034 import java.util.Iterator;
035 import java.util.List;
036 import java.util.Optional;
037 import java.util.function.Function;
038 import java.util.stream.Stream;
039 
040 import io.jenetics.util.Copyable;
041 
042 /**
043  * A general purpose node in a tree data-structure. The {@code TreeNode} is a
044  * mutable implementation of the {@link Tree} interface.
045  *
046  @param <T> the value type of the tree node
047  *
048  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
049  @version 5.2
050  @since 3.9
051  */
052 public final class TreeNode<T>
053     implements
054         Tree<T, TreeNode<T>>,
055         Iterable<TreeNode<T>>,
056         Copyable<TreeNode<T>>,
057         Serializable
058 {
059     @Serial
060     private static final long serialVersionUID = 2L;
061 
062     private T _value;
063     private TreeNode<T> _parent;
064     private List<TreeNode<T>> _children;
065 
066     /**
067      * Create a new tree node with no parent and children, but with the given
068      * user {@code value}.
069      *
070      @param value the user value of the new tree node
071      */
072     private TreeNode(final T value) {
073         _value = value;
074     }
075 
076 
077     /* *************************************************************************
078      * Basic operations
079      **************************************************************************/
080 
081     /**
082      * Sets the user object for this node.
083      *
084      @param value the node {@code value}
085      */
086     public void value(final T value) {
087         _value = value;
088     }
089 
090     /**
091      * Return the node value
092      *
093      @return the node value
094      */
095     @Override
096     public T value() {
097         return _value;
098     }
099 
100     /**
101      * Returns this node's parent if available.
102      *
103      @return the tree-node, or an empty value if this node has no parent
104      */
105     @Override
106     public Optional<TreeNode<T>> parent() {
107         return Optional.ofNullable(_parent);
108     }
109 
110     /**
111      * Sets this node's parent, but does not change the parent's child array.
112      * This method is called from {@code insert()} and {@code remove()} to
113      * reassign a child's parent, and it should not be messaged from anywhere
114      * else.
115      *
116      @param parent this node's new parent
117      */
118     void parent(final TreeNode<T> parent) {
119         _parent = parent;
120     }
121 
122     /**
123      * Returns the child at the specified index in this node's child array.
124      *
125      @param index   an index into this node's child array
126      @return the tree-node in this node's child array at the specified index
127      @throws ArrayIndexOutOfBoundsException  if the {@code index} is out of
128      *         bounds
129      */
130     @Override
131     public TreeNode<T> childAt(final int index) {
132         if (_children == null) {
133             throw new ArrayIndexOutOfBoundsException(format(
134                 "Child index is out of bounds: %s", index
135             ));
136         }
137 
138         return _children.get(index);
139     }
140 
141     @Override
142     public int childCount() {
143         return _children != null ? _children.size() 0;
144     }
145 
146     @Override
147     public Iterator<TreeNode<T>> childIterator() {
148         return _children != null
149             ? _children.iterator()
150             : Collections.emptyIterator();
151     }
152 
153     @Override
154     public Stream<TreeNode<T>> childStream() {
155         return _children != null
156             ? _children.stream()
157             : Stream.empty();
158     }
159 
160     /**
161      * Removes the {@code child} from its present parent (if it has one), sets
162      * the child's parent to this node, and then adds the child to this node's
163      * child array at index {@code index}. The new {@code child} must not be
164      * {@code null} and must not be an ancestor of {@code this} node.
165      *
166      @param index the index in the child array where this node is to be
167      *        inserted
168      @param child the sub-node to be inserted
169      @return {@code this} tree-node, for method chaining
170      @throws ArrayIndexOutOfBoundsException if {@code index} is out of bounds
171      @throws IllegalArgumentException if {@code child} is an ancestor of
172      *         {@code this} node
173      @throws NullPointerException if the given {@code child} is {@code null}
174      */
175     public TreeNode<T> insert(final int index, final TreeNode<T> child) {
176         requireNonNull(child);
177         if (isAncestor(child)) {
178             throw new IllegalArgumentException("The new child is an ancestor.");
179         }
180 
181         if (child._parent != null) {
182             child._parent.remove(child);
183         }
184 
185         child.parent(this);
186         createChildrenIfMissing();
187         _children.add(index, child);
188 
189         return this;
190     }
191 
192     // Only entry point for checking and creating non-existing children list.
193     private void createChildrenIfMissing() {
194         if (_children == null) {
195             _children = new ArrayList<>(2);
196         }
197     }
198 
199     /**
200      * Replaces the child at the give index with the given {@code child}
201      *
202      @param index the index of the child which will be replaced
203      @param child the new child
204      @return {@code this} tree-node, for method chaining
205      @throws ArrayIndexOutOfBoundsException  if the {@code index} is out of
206      *         bounds
207      @throws IllegalArgumentException if {@code child} is an ancestor of
208      *         {@code this} node
209      @throws NullPointerException if the given {@code child} is {@code null}
210      */
211     public TreeNode<T> replace(final int index, final TreeNode<T> child) {
212         requireNonNull(child);
213         if (_children == null) {
214             throw new ArrayIndexOutOfBoundsException(format(
215                 "Child index is out of bounds: %s", index
216             ));
217         }
218         if (isAncestor(child)) {
219             throw new IllegalArgumentException("The new child is an ancestor.");
220         }
221 
222         final TreeNode<T> oldChild = _children.set(index, child);
223         assert oldChild != null;
224         assert oldChild._parent == this;
225 
226         oldChild.parent(null);
227         child.parent(this);
228 
229         return this;
230     }
231 
232     /**
233      * Removes the child at the specified index from this node's children and
234      * sets that node's parent to {@code null}.
235      *
236      @param index the index in this node's child array of the child to remove
237      @return {@code this} tree-node, for method chaining
238      @throws ArrayIndexOutOfBoundsException  if the {@code index} is out of
239      *         bounds
240      */
241     public TreeNode<T> remove(final int index) {
242         if (_children == null) {
243             throw new ArrayIndexOutOfBoundsException(format(
244                 "Child index is out of bounds: %s", index
245             ));
246         }
247 
248         final TreeNode<T> child = _children.remove(index);
249         assert child._parent == this;
250         child.parent(null);
251 
252         if (_children.isEmpty()) {
253             _children = null;
254         }
255 
256         return this;
257     }
258 
259     /**
260      * Removes the child at the given {@code path}. If no child exists at the
261      * given path, nothing is removed.
262      *
263      @since 4.4
264      *
265      @param path the path of the child to replace
266      @return {@code true} if a child at the given {@code path} existed and
267      *         has been removed
268      @throws NullPointerException if one of the given argument is {@code null}
269      */
270     public boolean removeAtPath(final Path path) {
271         final Optional<TreeNode<T>> parent = childAtPath(path)
272             .flatMap(Tree::parent);
273 
274         parent.ifPresent(p -> p.remove(path.get(path.length() 1)));
275         return parent.isPresent();
276     }
277 
278     /**
279      * Replaces the child at the given {@code path} with the given new
280      * {@code child}. If no child exists at the given path, nothing is replaced.
281      *
282      @since 4.4
283      *
284      @param path the path of the child to replace
285      @param child the new child
286      @return {@code true} if a child at the given {@code path} existed and
287      *         has been replaced
288      @throws NullPointerException if one of the given argument is {@code null}
289      */
290     public boolean replaceAtPath(final Path path, final TreeNode<T> child) {
291         requireNonNull(path);
292         requireNonNull(child);
293 
294         final Optional<TreeNode<T>> old = childAtPath(path);
295         final Optional<TreeNode<T>> parent = old.flatMap(TreeNode::parent);
296 
297         parent.ifPresentOrElse(
298             p -> p.replace(path.get(path.length() 1), child),
299             () -> {
300                 removeAllChildren();
301                 value(child.value());
302 
303                 // Need to create a copy of the children, before attaching it.
304                 final List<TreeNode<T>> nodes = child._children != null
305                     ? List.copyOf(child._children)
306                     : List.of();
307 
308                 nodes.forEach(this::attach);
309             }
310         );
311 
312         return old.isPresent();
313     }
314 
315     /* *************************************************************************
316      * Derived operations
317      **************************************************************************/
318 
319     /**
320      * Detaches the subtree rooted at {@code this} node from the tree, giving
321      * {@code this} node a {@code null} parent. Does nothing if {@code this}
322      * node is the root of its tree.
323      *
324      @return {@code this}
325      */
326     public TreeNode<T> detach() {
327         if (_parent != null) {
328             _parent.remove(this);
329         }
330 
331         return this;
332     }
333 
334     /**
335      * Remove the {@code child} from {@code this} node's child array, giving it
336      * a {@code null} parent.
337      *
338      @param child the child of this node to remove
339      @throws NullPointerException if the given {@code child} is {@code null}
340      @throws IllegalArgumentException if the given {@code child} is not a
341      *         child of this node
342      */
343     public void remove(final Tree<?, ?> child) {
344         requireNonNull(child);
345 
346         if (!isChild(child)) {
347             throw new IllegalArgumentException("The given child is not a child.");
348         }
349         remove(indexOf(child));
350     }
351 
352     /**
353      * Removes all children fo {@code this} node and setting their parents to
354      * {@code null}. If {@code this} node has no children, this method does
355      * nothing.
356      */
357     public void removeAllChildren() {
358         if (_children != null) {
359             for (TreeNode<T> child : _children) {
360                 child.parent(null);
361             }
362 
363             _children = null;
364         }
365     }
366 
367     /**
368      * Remove the given {@code child} from its parent and makes it a child of
369      * this node by adding it to the end of this node's child array.
370      *
371      @param child the new child added to this node
372      @return {@code this} tree-node, for method chaining
373      @throws NullPointerException if the given {@code child} is {@code null}
374      */
375     public TreeNode<T> attach(final TreeNode<T> child) {
376         requireNonNull(child);
377 
378         if (child._parent == this) {
379             insert(childCount() 1, child);
380         else {
381             insert(childCount(), child);
382         }
383 
384         return this;
385     }
386 
387     /**
388      * Attaches the given {@code children} to {@code this} node.
389      *
390      @param children the children to attach to {@code this} node
391      @return {@code this} tree-node, for method chaining
392      @throws NullPointerException if the given {@code children} array is
393      *         {@code null}
394      */
395     @SafeVarargs
396     public final TreeNode<T> attach(final T... children) {
397         for (T child : children) {
398             attach(TreeNode.of(child));
399         }
400 
401         return this;
402     }
403 
404     /**
405      * Attaches the given {@code child} to {@code this} node.
406      *
407      @param child the child to attach to {@code this} node
408      @return {@code this} tree-node, for method chaining
409      */
410     public TreeNode<T> attach(final T child) {
411         return attach(TreeNode.of(child));
412     }
413 
414     @Override
415     public TreeNode<T> copy() {
416         return ofTree(this);
417     }
418 
419     /**
420      * Returns a new {@code TreeNode} consisting of all nodes of {@code this}
421      * tree, but with a different value type, created by applying the given
422      * function to the node values of {@code this} tree.
423      *
424      @param mapper the node value mapper
425      @param <B> the new node type
426      @return a new tree consisting of all nodes of {@code this} tree
427      @throws NullPointerException if the given {@code mapper} function is
428      *         {@code null}
429      */
430     public <B> TreeNode<B> map(final Function<? super T, ? extends B> mapper) {
431         final TreeNode<B> target = TreeNode.of(mapper.apply(value()));
432         copy(this, target, mapper);
433         return target;
434     }
435 
436 
437     @Override
438     public int hashCode() {
439         return Tree.hashCode(this);
440     }
441 
442     @Override
443     public boolean equals(final Object obj) {
444         return obj instanceof Tree<?, ?> other && Tree.equals(this, other);
445     }
446 
447     @Override
448     public String toString() {
449         return toParenthesesString();
450     }
451 
452 
453 
454     /* *************************************************************************
455      * Static factory methods.
456      **************************************************************************/
457 
458     /**
459      * Return a new {@code TreeNode} with a {@code null} tree value.
460      *
461      @param <T> the tree-node type
462      @return a new tree-node
463      */
464     public static <T> TreeNode<T> of() {
465         return TreeNode.of(null);
466     }
467 
468     /**
469      * Return a new {@code TreeNode} with the given node {@code value}.
470      *
471      @param value the node value
472      @param <T> the tree-node type
473      @return a new tree-node
474      */
475     public static <T> TreeNode<T> of(final T value) {
476         return new TreeNode<>(value);
477     }
478 
479     /**
480      * Return a new {@code TreeNode} from the given source {@code tree}. The
481      * whole tree is copied.
482      *
483      @param tree the source tree the new tree-node is created from
484      @param mapper the tree value mapper function
485      @param <T> the current tree value type
486      @param <B> the mapped tree value type
487      @return a new {@code TreeNode} from the given source {@code tree}
488      @throws NullPointerException if one of the arguments is {@code null}
489      */
490     public static <T, B> TreeNode<B> ofTree(
491         final Tree<? extends T, ?> tree,
492         final Function<? super T, ? extends B> mapper
493     ) {
494         final TreeNode<B> target = of(mapper.apply(tree.value()));
495         copy(tree, target, mapper);
496         return target;
497     }
498 
499     private static <T, B> void copy(
500         final Tree<? extends T, ?> source,
501         final TreeNode<B> target,
502         final Function<? super T, ? extends B> mapper
503     ) {
504         for (int i = 0; i < source.childCount(); ++i) {
505             final var child = source.childAt(i);
506             final TreeNode<B> targetChild = of(mapper.apply(child.value()));
507             target.attach(targetChild);
508             copy(child, targetChild, mapper);
509         }
510     }
511 
512     /**
513      * Return a new {@code TreeNode} from the given source {@code tree}. The
514      * whole tree is copied.
515      *
516      @param tree the source tree the new tree-node is created from
517      @param <T> the current tree value type
518      @return a new {@code TreeNode} from the given source {@code tree}
519      @throws NullPointerException if the source {@code tree} is {@code null}
520      */
521     public static <T> TreeNode<T> ofTree(final Tree<? extends T, ?> tree) {
522         return ofTree(tree, Function.identity());
523     }
524 
525     /**
526      * Parses a (parentheses) tree string, created with
527      {@link Tree#toParenthesesString()}. The tree string might look like this:
528      <pre>
529      *  mul(div(cos(1.0),cos(π)),sin(mul(1.0,z)))
530      </pre>
531      *
532      * The parse method doesn't strip the whitespace between the parentheses and
533      * the commas. If you want to remove this <em>formatting</em> whitespaces,
534      * you should do the parsing with an addition <em>mapper</em> function.
535      <pre>{@code
536      * final TreeNode<String> tree = TreeNode.parse(
537      *     "mul(  div(cos( 1.0) , cos(π )), sin(mul(1.0, z) ) )",
538      *     String::trim
539      * );
540      * }</pre>
541      * The code above will trim all tree nodes during the parsing process.
542      *
543      @see Tree#toParenthesesString(Function)
544      @see Tree#toParenthesesString()
545      @see TreeNode#parse(String, Function)
546      *
547      @since 4.3
548      *
549      @param tree the parentheses tree string
550      @return the parsed tree
551      @throws NullPointerException if the given {@code tree} string is
552      *         {@code null}
553      @throws IllegalArgumentException if the given tree string could not be
554      *         parsed
555      */
556     public static TreeNode<String> parse(final String tree) {
557         return ParenthesesTreeParser.parse(tree, Function.identity());
558     }
559 
560     /**
561      * Parses a (parentheses) tree string, created with
562      {@link Tree#toParenthesesString()}. The tree string might look like this
563      <pre>
564      *  0(1(4,5),2(6),3(7(10,11),8,9))
565      </pre>
566      * and can be parsed to an integer tree with the following code:
567      <pre>{@code
568      * final Tree<Integer, ?> tree = TreeNode.parse(
569      *     "0(1(4,5),2(6),3(7(10,11),8,9))",
570      *     Integer::parseInt
571      * );
572      * }</pre>
573      *
574      @see Tree#toParenthesesString(Function)
575      @see Tree#toParenthesesString()
576      @see TreeNode#parse(String)
577      *
578      @since 4.3
579      *
580      @param <B> the tree node value type
581      @param tree the parentheses tree string
582      @param mapper the mapper which converts the serialized string value to
583      *        the desired type
584      @return the parsed tree object
585      @throws NullPointerException if one of the arguments is {@code null}
586      @throws IllegalArgumentException if the given parentheses tree string
587      *         doesn't represent a valid tree
588      */
589     public static <B> TreeNode<B> parse(
590         final String tree,
591         final Function<? super String, ? extends B> mapper
592     ) {
593         return ParenthesesTreeParser.parse(tree, mapper);
594     }
595 
596 
597     /* *************************************************************************
598      *  Java object serialization
599      * ************************************************************************/
600 
601     @Serial
602     private Object writeReplace() {
603         return new SerialProxy(SerialProxy.TREE_NODE, this);
604     }
605 
606     @Serial
607     private void readObject(final ObjectInputStream stream)
608         throws InvalidObjectException
609     {
610         throw new InvalidObjectException("Serialization proxy required.");
611     }
612 
613 
614     void write(final ObjectOutput outthrows IOException {
615         FlatTreeNode.ofTree(this).write(out);
616     }
617 
618     @SuppressWarnings("unchecked")
619     static Object read(final ObjectInput in)
620         throws IOException, ClassNotFoundException
621     {
622         return TreeNode.ofTree(FlatTreeNode.read(in));
623     }
624 
625 }