001/* 002 * Java Genetic Algorithm Library (jenetics-7.0.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 */ 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.stream.Stream; 039 040import io.jenetics.util.Copyable; 041import io.jenetics.util.ISeq; 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 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 at 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 argument 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 at 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 argument 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 if (parent.isPresent()) { 299 parent.orElseThrow(AssertionError::new) 300 .replace(path.get(path.length() - 1), child); 301 } else { 302 removeAllChildren(); 303 value(child.value()); 304 305 final ISeq<TreeNode<T>> nodes = child.childStream() 306 .collect(ISeq.toISeq()); 307 308 for (TreeNode<T> node : nodes) { 309 attach(node); 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 source.childStream().forEachOrdered(child -> { 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 out) throws 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}