filter method

  1. @override
Iterable<WidgetTreeNode> filter(
  1. Iterable<WidgetTreeNode> candidates
)
override

Filters all candidates, retuning only a subset that matches

Implementation

@override
Iterable<WidgetTreeNode> filter(Iterable<WidgetTreeNode> candidates) {
  if (childSelectors.isEmpty) {
    return candidates;
  }
  final tree = currentWidgetTreeSnapshot();

  // First check all negate selectors (where maxQuantity == 0)
  final negates = childSelectors.where((e) => e.quantityConstraint.max == 0);
  for (final negate in negates) {
    final s = snapshot(negate, validateQuantity: false);
    if (s.discovered.isNotEmpty) {
      // this negate selector matches, which it shouldn't
      return [];
    }
  }

  final List<WidgetTreeNode> matchingChildNodes = [];

  // Then check for every queryMatch if the children and props match
  for (final WidgetTreeNode candidate in candidates) {
    final Map<WidgetSelector, List<WidgetTreeNode>> matchesPerChild = {};

    final ScopedWidgetTreeSnapshot subtree = tree.scope(candidate);
    final List<WidgetTreeNode> subtreeNodes = subtree.allNodes;
    for (final WidgetSelector<Widget> childSelector
        in childSelectors - negates) {
      matchesPerChild[childSelector] = [];
      // TODO instead of searching the children, starting from the root widget, find a way to reverse the search and
      //  start form the subtree.
      //  Keep in mind, each child selector might be defined with parents which are outside of the subtree
      final WidgetSnapshot ss =
          snapshot(childSelector, validateQuantity: false);

      final minConstraint = childSelector.quantityConstraint.min;
      final maxConstraint = childSelector.quantityConstraint.max;

      final discoveredInSubtree =
          ss.discovered.where((node) => subtreeNodes.contains(node)).toList();

      if (minConstraint == null &&
          maxConstraint == null &&
          discoveredInSubtree.isEmpty) {
        // This childSelector doesn't match any child
        continue;
      }

      if (minConstraint != null &&
          minConstraint > discoveredInSubtree.length) {
        // not a match found less than the minimum required
        continue;
      }
      if (maxConstraint != null &&
          maxConstraint < discoveredInSubtree.length) {
        // not a match because found more than the maximum allowed
        continue;
      }

      // consider it as a match
      matchesPerChild[childSelector] = discoveredInSubtree;
    }
    // TODO early exist the loop above instead of adding an empty list to the map
    if (matchesPerChild.values.any((list) => list.isEmpty)) {
      // not all children match
      continue;
    }
    matchingChildNodes.add(candidate);
  }
  return matchingChildNodes;
}