docs: private List<TopLevelItem> getItems(boolean recurse) {
          SortedSet<String> names;
          List<TopLevelItem> items = new ArrayList<>();
  
          synchronized (this) {
              names = new TreeSet<>(jobNames);
          }
  
          ItemGroup<? extends TopLevelItem> parent = getOwner().getItemGroup();
          List<TopLevelItem> parentItems = new ArrayList<>(parent.getItems());
          includeItems(parent, parentItems, names);
  
          Boolean statusFilter = this.statusFilter; // capture the value to isolate us from concurrent update
          Iterable<? extends TopLevelItem> candidates;
          if (recurse) {
              candidates = parent.getAllItems(TopLevelItem.class);
          } else {
              candidates = parent.getItems();
          }
          for (TopLevelItem item : candidates) {
              if (!names.contains(item.getRelativeNameFrom(getOwner().getItemGroup()))) continue;
              // Add if no status filter or filter matches enabled/disabled status:
              if(statusFilter == null || !(item instanceof ParameterizedJobMixIn.ParameterizedJob) // TODO or better to call the more generic Job.isBuildable?
                                || ((ParameterizedJobMixIn.ParameterizedJob)item).isDisabled() ^ statusFilter)
                  items.add(item);
          }
  
          // check the filters
          Iterable<ViewJobFilter> jobFilters = getJobFilters();
          List<TopLevelItem> allItems = new ArrayList<>(parentItems);
          if (recurse) allItems = expand(allItems, new ArrayList<>());
      	for (ViewJobFilter jobFilter: jobFilters) {
      		items = jobFilter.filter(items, allItems, this);
      	}
          // for sanity, trim off duplicates
          items = new ArrayList<>(new LinkedHashSet<>(items));
          
          return items;
      }