public static void copyDirectory(Path source, final Path dest) throws IOException {
        if ((source == null) || (dest == null) || !Files.exists(source, LinkOption.NOFOLLOW_LINKS)
                || !Files.isDirectory(source, LinkOption.NOFOLLOW_LINKS)) {
            throw new InvalidParameterException();
        }

        Files.walkFileTree(source, new FileVisitor<Path>() {
            Path tmp = dest;

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                this.tmp = this.tmp.resolve(dir.getFileName());
                if (!Files.exists(this.tmp, LinkOption.NOFOLLOW_LINKS)) {
                    Files.createDirectory(this.tmp);
                }
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                if (this.tmp.getFileName().equals(file.getParent().getFileName())) {
                    Files.copy(file, this.tmp.resolve(file.getFileName()), StandardCopyOption.REPLACE_EXISTING);
                } else {
                    Files.copy(file, this.tmp.getParent().resolve(file.getFileName()), StandardCopyOption.REPLACE_EXISTING);
                }
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                return FileVisitResult.TERMINATE;
            }
        });
    }