GuiTraverser.java
package swingtree;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import java.awt.Component;
import java.awt.Container;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* Traverses the component tree starting from a given component and finds all components
* that match a supplied predicate and type.
*/
final class GuiTraverser
{
private static final Logger log = org.slf4j.LoggerFactory.getLogger(GuiTraverser.class);
private final Component _current;
GuiTraverser( final Component current ) {
Objects.requireNonNull(current);
_current = current;
}
<C extends Component> Stream<C> find( final Class<C> type, final Predicate<C> predicate ) {
return _find( c -> {
boolean isType = type.isAssignableFrom(c.getClass());
if ( !isType )
return false;
try {
return predicate.test(type.cast(c));
} catch (Exception e) {
log.error(
"An exception occurred while testing a component of type '{}'!",
type.getSimpleName(), e
);
return false;
}
})
.map( type::cast );
}
private Stream<Component> _find( Predicate<Component> predicate ) {
List<Component> roots = traverseUpwardsAndFindAllRoots(_current, new ArrayList<>());
return roots.stream()
.flatMap( c -> _traverseDownwardsAndFind(c, predicate).stream() );
}
private List<Component> traverseUpwardsAndFindAllRoots(
final Component component,
final List<Component> roots
) {
Component parent = _findRootParentOf(component);
roots.add(parent);
Container grandParent = parent.getParent();
if ( grandParent != null ) {
return traverseUpwardsAndFindAllRoots(grandParent, roots);
}
else
return roots;
}
private Component _findRootParentOf( final Component component ) {
Container parent = component.getParent();
if ( _acknowledgesParenthood( parent, component ) )
return _findRootParentOf( parent );
else
return component;
}
private boolean _acknowledgesParenthood( final Component parent, final Component child ) {
if ( parent instanceof Container ) {
Container container = (Container) parent;
for ( Component component : container.getComponents() )
if ( component == child )
return true;
}
return false;
}
private List<Component> _traverseDownwardsAndFind( final Component cmp, final Predicate<Component> predicate )
{
List<Component> found = new ArrayList<>();
_traverseDownwardsAndFind(cmp, predicate, found);
return found;
}
private void _traverseDownwardsAndFind(
final @Nullable Component cmp,
final Predicate<Component> predicate,
final List<Component> found
) {
if( cmp == null )
return; // Not a container, return
// Add this component
if ( predicate.test(cmp) && !found.contains(cmp) )
found.add(cmp);
if ( cmp instanceof Container ) { // A container, let's traverse it.
Container container = (Container) cmp;
// Go visit and add all children
for ( Component subComponent : container.getComponents() )
_traverseDownwardsAndFind(subComponent, predicate, found);
}
}
}