class AttributeDelegator extends BaseDelegator implements IAttributeDelegator {

    @Override
    public List<IFileSpec> setFileAttributes(
            final List<IFileSpec> files,
            @Nonnull final Map<String, String> attributes,
            final SetFileAttributesOptions opts) throws P4JavaException {

        Validate.notNull(attributes);

        /*
         * Note the rather odd parameter processing below, required due to the
         * rather odd way attributes are passed to the server (or not) -- each
         * name must have a -n flag attached, and each value a corresponding -v
         * flag; it's unclear what happens when these don't match up, but never
         * mind... in any case, after some experimentation, it seems safest to
         * bunch all the names first, then the values. This may change with
         * further experimentation.
         */
        List<String> args = new ArrayList<>();
        for (String name : attributes.keySet()) {
            args.add("-n" + name);
        }

        for (Map.Entry<String, String> entry : attributes.entrySet()) {
            if (isNotBlank(entry.getValue())) {
                args.add("-v" + entry.getValue());
            }
        }

        /*
         * Note that this is the one command that doesn't adhere to the
         * (admittedly loose) rules about multiple returns and tag names, etc,
         * meaning that we anomalously return a list of result strings rather
         * than file specs; the reason underlying this is that each attribute
         * set causes a result row, meaning we may get multiple results back
         * from the server for the same file, and several error messages, etc.
         * -- all from the single request. This seems less than optimal to me...
         */
        List<Map<String, Object>> resultMaps = execMapCmdList(
                ATTRIBUTE,
                processParameters(
                        opts,
                        files,
                        args.toArray(new String[args.size()]),
                        true,
                        server),
                null);

        return buildSetFileAttributesFileSpecsFromCommandResultMaps(
                resultMaps,
                rethrowFunction(
                        new FunctionWithException<Map<String, Object>, IFileSpec>() {
                            @Override
                            public IFileSpec apply(Map<String, Object> map) throws P4JavaException {
                                return ResultListBuilder.handleFileReturn(map, server);
                            }
                        }
                )
        );
    }

    public  AttributeDelegator(IOptionsServer server);

}