@Override
    public List<Path> run(final Session<?> session) throws BackgroundException {
        final Delete delete;
        if(trash) {
            if(null == session.getFeature(Trash.class)) {
                log.warn(String.format("No trash feature available for %s", session));
                delete = session.getFeature(Delete.class);
            }
            else {
                delete = session.getFeature(Trash.class);
            }
        }
        else {
            delete = session.getFeature(Delete.class);
        }
        final ListService list = session.getFeature(ListService.class);
        final Map<Path, TransferStatus> recursive = new LinkedHashMap<>();
        for(Path file : files) {
            if(this.isCanceled()) {
                throw new ConnectionCanceledException();
            }
            recursive.putAll(this.compile(delete, list, new WorkerListProgressListener(this, listener), file));
        }
        // Iterate again to delete any files that can be omitted when recursive operation is supported
        if(delete.isRecursive()) {
            recursive.keySet().removeIf(f -> recursive.keySet().stream().anyMatch(f::isChild));
        }
        final HostPreferences preferences = new HostPreferences(session.getHost());
        if(preferences.getBoolean("versioning.enable") && preferences.getBoolean("versioning.delete.enable")) {
            switch(session.getHost().getProtocol().getVersioningMode()) {
                case custom:
                    final Versioning versioning = session.getFeature(Versioning.class);
                    if(versioning != null) {
                        for(Iterator<Path> iter = recursive.keySet().iterator(); iter.hasNext(); ) {
                            final Path f = iter.next();
                            if(versioning.getConfiguration(f).isEnabled()) {
                                if(versioning.save(f)) {
                                    if(log.isDebugEnabled()) {
                                        log.debug(String.format("Skip deleting %s", f));
                                    }
                                    iter.remove();
                                }
                            }
                        }
                    }
            }
        }
        if(!recursive.isEmpty()) {
            delete.delete(recursive, prompt, new Delete.Callback() {
                @Override
                public void delete(final Path file) {
                    listener.message(MessageFormat.format(LocaleFactory.localizedString("Deleting {0}", "Status"), file.getName()));
                    callback.delete(file);
                    if(file.isDirectory()) {
                        if(delete.isRecursive()) {
                            files.stream().filter(f -> f.isChild(file)).forEach(callback::delete);
                        }
                    }
                }
            });
        }
        return new ArrayList<>(recursive.keySet());
    }
@Test
    public void testCompileDefault() throws Exception {
        final Session session = new NullSession(new Host(new TestProtocol())) {
            @Override
            @SuppressWarnings("unchecked")
            public <T> T _getFeature(final Class<T> type) {
                if(type == Delete.class) {
                    return (T) new Delete() {
                        @Override
                        public void delete(final Map<Path, TransferStatus> files, final PasswordCallback prompt, final Callback callback) {
                            assertEquals(new Path("/t/a", EnumSet.of(Path.Type.file)), new ArrayList<>(files.keySet()).get(0));
                            assertEquals(new Path("/t/d/b", EnumSet.of(Path.Type.file)), new ArrayList<>(files.keySet()).get(1));
                            assertEquals(new Path("/t/d", EnumSet.of(Path.Type.directory)), new ArrayList<>(files.keySet()).get(2));
                            assertEquals(new Path("/t", EnumSet.of(Path.Type.directory)), new ArrayList<>(files.keySet()).get(3));
                        }
                    };
                }
                return super._getFeature(type);
            }

            @Override
            public AttributedList<Path> list(final Path file, final ListProgressListener listener) {
                if(file.equals(new Path("/t", EnumSet.of(Path.Type.directory)))) {
                    return new AttributedList<>(Arrays.asList(
                            new Path("/t/a", EnumSet.of(Path.Type.file)),
                            new Path("/t/d", EnumSet.of(Path.Type.directory))
                    ));
                }
                if(file.equals(new Path("/t/d", EnumSet.of(Path.Type.directory)))) {
                    return new AttributedList<>(Collections.singletonList(
                            new Path("/t/d/b", EnumSet.of(Path.Type.file))
                    ));
                }
                fail();
                return null;
            }
        };
        final DeleteWorker worker = new DeleteWorker(new DisabledLoginCallback(),
                Collections.singletonList(new Path("/t", EnumSet.of(Path.Type.directory))),
                new DisabledProgressListener());
        int hashCode = worker.hashCode();
        assertEquals(4, worker.run(session).size());
        assertEquals(hashCode, worker.hashCode());
    }