@Override
    public Path copy(final Path file, final Path target, final TransferStatus status, final ConnectionCallback callback, final StreamListener listener) throws BackgroundException {
        try {
            final EueApiClient client = new EueApiClient(session);
            if(status.isExists()) {
                if(log.isWarnEnabled()) {
                    log.warn(String.format("Trash file %s to be replaced with %s", target, file));
                }
                new EueTrashFeature(session, fileid).delete(Collections.singletonMap(target, status), callback, new Delete.DisabledCallback());
            }
            final String resourceId = fileid.getFileId(file);
            final String parentResourceId = fileid.getFileId(target.getParent());
            String targetResourceId = null;
            final ResourceCopyResponseEntries resourceCopyResponseEntries;
            switch(parentResourceId) {
                case EueResourceIdProvider.ROOT:
                case EueResourceIdProvider.TRASH:
                    resourceCopyResponseEntries = new CopyChildrenForAliasApiApi(client)
                            .resourceAliasAliasChildrenCopyPost(parentResourceId,
                                    Collections.singletonList(String.format("%s/resource/%s",
                                            session.getBasePath(), resourceId)), null, null, null,
                                    "rename", null);
                    break;
                default:
                    resourceCopyResponseEntries = new CopyChildrenApi(client).resourceResourceIdChildrenCopyPost(parentResourceId,
                            Collections.singletonList(String.format("%s/resource/%s", session.getBasePath(), resourceId)), null, null, null,
                            "rename", null);
            }
            if(null == resourceCopyResponseEntries) {
                // Copy of single file will return 200 status code with empty response body
            }
            else {
                for(ResourceCopyResponseEntry resourceCopyResponseEntry : resourceCopyResponseEntries.values()) {
                    switch(resourceCopyResponseEntry.getStatusCode()) {
                        case HttpStatus.SC_CREATED:
                            fileid.cache(target, EueResourceIdProvider.getResourceIdFromResourceUri(resourceCopyResponseEntry.getHeaders().getLocation()));
                            break;
                        default:
                            log.warn(String.format("Failure %s copying file %s", resourceCopyResponseEntries, file));
                            throw new EueExceptionMappingService().map(new ApiException(resourceCopyResponseEntry.getReason(),
                                    null, resourceCopyResponseEntry.getStatusCode(), client.getResponseHeaders()));
                    }
                }

            }
            listener.sent(status.getLength());
            if(!StringUtils.equals(file.getName(), target.getName())) {
                final ResourceUpdateModel resourceUpdateModel = new ResourceUpdateModel();
                final ResourceUpdateModelUpdate resourceUpdateModelUpdate = new ResourceUpdateModelUpdate();
                final Uifs uifs = new Uifs();
                uifs.setName(target.getName());
                resourceUpdateModelUpdate.setUifs(uifs);
                resourceUpdateModel.setUpdate(resourceUpdateModelUpdate);
                final ResourceMoveResponseEntries resourceMoveResponseEntries = new UpdateResourceApi(client).resourceResourceIdPatch(fileid.getFileId(target),
                        resourceUpdateModel, null, null, null);
                if(null == resourceMoveResponseEntries) {
                    // Move of single file will return 200 status code with empty response body
                }
                else {
                    for(ResourceMoveResponseEntry resourceMoveResponseEntry : resourceMoveResponseEntries.values()) {
                        switch(resourceMoveResponseEntry.getStatusCode()) {
                            case HttpStatus.SC_CREATED:
                                break;
                            default:
                                log.warn(String.format("Failure %s renaming file %s", resourceMoveResponseEntry, file));
                                throw new EueExceptionMappingService().map(new ApiException(resourceMoveResponseEntry.getReason(),
                                        null, resourceMoveResponseEntry.getStatusCode(), client.getResponseHeaders()));
                        }
                    }
                }
            }
            return target;
        }
        catch(ApiException e) {
            throw new EueExceptionMappingService().map("Cannot copy {0}", e, file);
        }
    }
@Test
    public void testCopyRecursiveToRoot() throws Exception {
        final EueResourceIdProvider fileid = new EueResourceIdProvider(session);
        final Path sourceFolder = new EueDirectoryFeature(session, fileid).mkdir(
                new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
        final Path sourceFile = new Path(sourceFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
        createFile(fileid, sourceFile, RandomUtils.nextBytes(541));
        final Path targetFolder = new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
        assertNull(targetFolder.attributes().getFileId());
        final EueCopyFeature feature = new EueCopyFeature(session, fileid);
        feature.copy(sourceFolder, targetFolder, new TransferStatus(), new DisabledLoginCallback(), new DisabledStreamListener());
        assertNotEquals(sourceFolder.attributes().getFileId(), targetFolder.attributes().getFileId());
        assertTrue(new EueFindFeature(session, fileid).find(targetFolder));
        assertTrue(new EueFindFeature(session, fileid).find(new Path(targetFolder, sourceFile.getName(), sourceFile.getType())));
        assertTrue(new DefaultFindFeature(session).find(new Path(targetFolder, sourceFile.getName(), sourceFile.getType())));
        assertTrue(new EueFindFeature(session, fileid).find(sourceFolder));
        assertTrue(new EueFindFeature(session, fileid).find(sourceFile));
        assertTrue(new DefaultFindFeature(session).find(sourceFile));
        new EueDeleteFeature(session, fileid).delete(Arrays.asList(sourceFolder, targetFolder), new DisabledLoginCallback(), new Delete.DisabledCallback());
    }