class AdvancedFilterValidator implements GenericMetaInfDataValidator, FilterValidator, DocumentViewXmlValidator, JcrPathValidator {

    @Override
    public Collection<ValidationMessage> validate(@NotNull WorkspaceFilter filter) {
        if (isSubPackage) {
            return null; // not relevant for sub packages
        }
        Collection<ValidationMessage> messages = new LinkedList<>();
        messages.addAll(validatePathFilterSets(filter.getFilterSets(), true));
        messages.addAll(validatePathFilterSets(filter.getPropertyFilterSets(), false));

        // first collect all ancestors (except for the valid roots)
        Set<String> ancestors = new LinkedHashSet<>();
        for (PathFilterSet set : filter.getFilterSets()) {
            if ("cleanup".equals(set.getType())) {
                continue;
            }
            String root = StringUtils.substringBeforeLast(set.getRoot(), "/");
            // ignore well known roots
            if (validRoots.contains(root)) {
                continue;
            }

            // check if this package already contains the ancestor
            if (filter.contains(root)) {
                continue;
            }
            ancestors.add(root);
        }
        // then check for each ancestor
        for (String root : ancestors) {
            String coveringPackageId = null;
            boolean isContained = false;
            for (PackageInfo dependencyInfo : dependenciesMetaInfo) {
                WorkspaceFilter dependencyFilter = dependencyInfo.getFilter();
                if (dependencyFilter.contains(root)) {
                    isContained = true;
                }
                if (dependencyFilter.covers(root)) {
                    coveringPackageId = dependencyInfo.getId().toString();
                }
            }
            if (!isContained) {
                String msg;
                if (coveringPackageId == null) {
                    msg = String.format(MESSAGE_FILTER_ROOT_ANCESTOR_UNCOVERED, root);
                } else {
                    msg = String.format(MESSAGE_FILTER_ROOT_ANCESTOR_COVERED_BUT_EXCLUDED, root, coveringPackageId);
                }
                messages.add(new ValidationMessage(severityForUncoveredFilterRootAncestors, msg));
            }
        }
        return messages;
    }

    public  AdvancedFilterValidator(@NotNull DocumentBuilderFactory factory, @NotNull ValidationMessageSeverity defaultSeverity, @NotNull ValidationMessageSeverity severityForUncoveredAncestorNodes, @NotNull ValidationMessageSeverity severityForUncoveredFilterRootAncestors, @NotNull ValidationMessageSeverity severityForOrphanedFilterEntries, boolean isSubPackage, @NotNull Collection<PackageInfo> dependenciesMetaInfo, @NotNull WorkspaceFilter filter, @NotNull Collection<String> validRoots);

}