class PrivilegeXmlExporter {

    public static void writePrivileges(final WriterOpener writerOpener,
                                       final Session session,
                                       final List<String> onlyNames,
                                       final boolean includeBuiltins)
            throws RepositoryException, IOException {

        final Workspace workspace = session.getWorkspace();
        if (!(workspace instanceof JackrabbitWorkspace)) {
            throw new RepositoryException("Workspace must be instance of JackrabbitWorkspace, but isn't. type: " +
                    workspace.getClass().getName());
        }

        // get a name mapper function
        final NamePathResolver resolver = new DefaultNamePathResolver(session);
        final Function<String, Name> mapper = Fun.tryOrDefault1(resolver::getQName, null);
        // first resolve the builtins to qualified Names
        final Set<Name> builtinPrivileges = JsonCnd.BUILTIN_PRIVILEGES.stream()
                .map(mapper).filter(Objects::nonNull).collect(toSet());
        // create the builtinFilter for reuse
        final Predicate<Name> builtinFilter = includeBuiltins
                ? name -> true
                : ((Predicate<Name>) builtinPrivileges::contains).negate();
        // construct the desiredPrivileges set based on the names argument, filtering by the above builtinFilter
        final Set<Name> explicitNames = (onlyNames != null)
                ? onlyNames.stream().map(mapper).filter(Objects::nonNull).filter(builtinFilter).collect(toSet())
                : Collections.emptySet();
        // finally construct the privilegeFilter, which either includes only the desired
        final Predicate<Name> shouldInclude = !explicitNames.isEmpty()
                ? explicitNames::contains
                : builtinFilter;

        final PrivilegeManager privilegeManager = ((JackrabbitWorkspace) workspace).getPrivilegeManager();
        final List<PrivilegeDefinition> privilegeDefinitions = new ArrayList<>();
        for (Privilege privilege : privilegeManager.getRegisteredPrivileges()) {
            if (!shouldInclude.test(mapper.apply(privilege.getName()))) {
                continue;
            }
            final PrivilegeDefinitionImpl privilegeDefinition =
                    new PrivilegeDefinitionImpl(
                            mapper.apply(privilege.getName()),
                            privilege.isAbstract(),
                            Stream.of(privilege.getDeclaredAggregatePrivileges())
                                    .map(Privilege::getName)
                                    .map(mapper)
                                    .filter(Objects::nonNull)
                                    .collect(toSet()));
            privilegeDefinitions.add(privilegeDefinition);
        }

        // identify only those namespaces referenced by included privilege names or their aggregate members
        final Set<String> uris = privilegeDefinitions.stream()
                .flatMap(def -> Stream.concat(Stream.of(def.getName()), def.getDeclaredAggregateNames().stream()))
                .map(Name::getNamespaceURI).collect(toSet());

        final Map<String, String> namespaces = uris.stream()
                .collect(toMap(Fun.tryOrDefault1(session::getNamespacePrefix, null),
                        Function.identity()));

        PrivilegeDefinitionWriter pdWriter = new PrivilegeDefinitionWriter("text/xml");
        try (Writer writer = writerOpener.open()) {
            pdWriter.writeDefinitions(writer, privilegeDefinitions.toArray(new PrivilegeDefinition[0]), namespaces);
        }
    }

    private  PrivilegeXmlExporter();

}