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