docs: protected Tree traverseLocate(Tree[] daughterTrees, String[] how, boolean lastResort) {
      int headIdx = 0;
      String childCat;
      boolean found = false;
  
      if (how[0].equals("left")) {
        twoloop:
          for (int i = 1; i < how.length; i++) {
            for (headIdx = 0; headIdx < daughterTrees.length; headIdx++) {
              childCat = tlp.basicCategory(daughterTrees[headIdx].label().value());
              if (how[i].equals(childCat)) {
                found = true;
                break twoloop;
              }
            }
          }
      } else if (how[0].equals("leftdis")) {
        twoloop:
          for (headIdx = 0; headIdx < daughterTrees.length; headIdx++) {
            childCat = tlp.basicCategory(daughterTrees[headIdx].label().value());
            for (int i = 1; i < how.length; i++) {
              if (how[i].equals(childCat)) {
                found = true;
                break twoloop;
              }
            }
          }
      } else if (how[0].equals("right")) {
        // from right
        twoloop:
          for (int i = 1; i < how.length; i++) {
            for (headIdx = daughterTrees.length - 1; headIdx >= 0; headIdx--) {
              childCat = tlp.basicCategory(daughterTrees[headIdx].label().value());
              if (how[i].equals(childCat)) {
                found = true;
                break twoloop;
              }
            }
          }
      } else if (how[0].equals("rightdis")) {
        // from right, but search for any, not in turn
        twoloop:
          for (headIdx = daughterTrees.length - 1; headIdx >= 0; headIdx--) {
            childCat = tlp.basicCategory(daughterTrees[headIdx].label().value());
            for (int i = 1; i < how.length; i++) {
              if (how[i].equals(childCat)) {
                found = true;
                break twoloop;
              }
            }
          }
      } else if (how[0].equals("leftexcept")) {
        for (headIdx = 0; headIdx < daughterTrees.length; headIdx++) {
          childCat = tlp.basicCategory(daughterTrees[headIdx].label().value());
          found = true;
          for (int i = 1; i < how.length; i++) {
            if (how[i].equals(childCat)) {
              found = false;
            }
          }
          if (found) {
            break;
          }
        }
      } else if (how[0].equals("rightexcept")) {
        for (headIdx = daughterTrees.length - 1; headIdx >= 0; headIdx--) {
          childCat = tlp.basicCategory(daughterTrees[headIdx].label().value());
          found = true;
          for (int i = 1; i < how.length; i++) {
            if (how[i].equals(childCat)) {
              found = false;
            }
          }
          if (found) {
            break;
          }
        }
      } else {
        throw new RuntimeException("ERROR: invalid direction type " + how[0] + " to nonTerminalInfo map in AbstractCollinsHeadFinder.");
      }
      
      // what happens if our rule didn't match anything
      if (!found) {
        if (lastResort) {
          // use the default rule to try to match anything except categoriesToAvoid
          // if that doesn't match, we'll return the left or rightmost child (by
          // setting headIdx).  We want to be careful to ensure that postOperationFix
          // runs exactly once.
          String[] rule;
          if (how[0].startsWith("left")) {
            headIdx = 0;
            rule = defaultLeftRule;
          } else {
            headIdx = daughterTrees.length - 1;
            rule = defaultRightRule;
          }
          Tree child = traverseLocate(daughterTrees, rule, false);
          if (child != null) {
            return child;
          }
        } else {
          // if we're not the last resort, we can return null to let the next rule try to match
          return null;
        }
      }
      
      headIdx = postOperationFix(headIdx, daughterTrees);
  
      return daughterTrees[headIdx];
    }