package org.codefx.libfx.collection.tree.navigate; import java.awt.Component; import java.awt.Container; import java.util.Objects; import java.util.Optional; import java.util.OptionalInt; /** * A {@link TreeNavigator} for an AWT component hierarchy. * <p> * This implementation is thread-safe in the sense that individual method calls will not fail if the component hierarchy * is changed concurrently. But it can not prevent return values from getting stale so chaining calls might lead to * unexpected results, e.g.: * * <pre> * Component component = ... * if (getParent(component).isPresent()) { * // the component has a parent, so it should have a child index, too; * // but the component hierarchy may have changed, so 'indexPresent' may be false * boolean indexPresent = getChildIndex(component).isPresent(); * } * </pre> * Similarly: * * <pre> * Component parent = ... * Optional<Component> child1 = getChild(parent, 0); * Optional<Component> child2 = getChild(parent, 0); * // if the component hierarchy changed between the two calls, this may be false * boolean sameChildren = child1.equals(child2); * </pre> */ public class ComponentHierarchyNavigator implements TreeNavigator<Component> { @Override public Optional<Component> getParent(Component child) { Objects.requireNonNull(child, "The argument 'child' must not be null."); return Optional.ofNullable(child.getParent()); } @Override public OptionalInt getChildIndex(Component node) { Objects.requireNonNull(node, "The argument 'node' must not be null."); Component parent = node.getParent(); if (parent == null) return OptionalInt.empty(); if (!(parent instanceof Container)) return OptionalInt.empty(); Component[] siblings = ((Container) parent).getComponents(); return getIndex(node, siblings); } private static OptionalInt getIndex(Component node, Component[] siblings) { try { for (int i = 0; i < siblings.length; i++) if (siblings[i] == node) return OptionalInt.of(i); return OptionalInt.empty(); } catch (ArrayIndexOutOfBoundsException ex) { return OptionalInt.empty(); } } @Override public int getChildrenCount(Component parent) { Objects.requireNonNull(parent, "The argument 'parent' must not be null."); if (!(parent instanceof Container)) return 0; return ((Container) parent).getComponents().length; } @Override public Optional<Component> getChild(Component parent, int childIndex) { Objects.requireNonNull(parent, "The argument 'parent' must not be null."); if (childIndex < 0) throw new IllegalArgumentException("The argument 'childIndex' must be non-negative."); if (!(parent instanceof Container)) return Optional.empty(); return getChild((Container) parent, childIndex); } private static Optional<Component> getChild(Container parent, int childIndex) { if (parent.getComponents().length <= childIndex) return Optional.empty(); try { // even though we checked first, due to threading this might fail return Optional.of(parent.getComponent(childIndex)); } catch (ArrayIndexOutOfBoundsException ex) { return Optional.empty(); } } }