@Override
    public AttributedList<Path> read(final Path directory, final List<String> replies) throws FTPInvalidListException {
        final AttributedList<Path> children = new AttributedList<Path>();
        // At least one entry successfully parsed
        boolean success = false;
        // Call hook for those implementors which need to perform some action upon the list after it has been created
        // from the server stream, but before any clients see the list
        parser.preParse(replies);
        for(String line : replies) {
            final FTPFile f = parser.parseFTPEntry(line);
            if(null == f) {
                continue;
            }
            final String name = f.getName();
            if(!success) {
                if(lenient) {
                    // Workaround for #2410. STAT only returns ls of directory itself
                    // Workaround for #2434. STAT of symbolic link directory only lists the directory itself.
                    if(directory.getName().equals(name)) {
                        log.warn(String.format("Skip %s matching parent directory name", f.getName()));
                        continue;
                    }
                    if(name.contains(String.valueOf(Path.DELIMITER))) {
                        if(!name.startsWith(directory.getAbsolute() + Path.DELIMITER)) {
                            // Workaround for #2434.
                            log.warn(String.format("Skip %s with delimiter in name", name));
                            continue;
                        }
                    }
                }
            }
            success = true;
            if(name.equals(".") || name.equals("..")) {
                if(log.isDebugEnabled()) {
                    log.debug(String.format("Skip %s", f.getName()));
                }
                continue;
            }
            final Path parsed = new Path(directory, PathNormalizer.name(name), f.getType() == FTPFile.DIRECTORY_TYPE ? EnumSet.of(Path.Type.directory) : EnumSet.of(Path.Type.file));
            switch(f.getType()) {
                case FTPFile.SYMBOLIC_LINK_TYPE:
                    parsed.setType(EnumSet.of(Path.Type.file, Path.Type.symboliclink));
                    // Symbolic link target may be an absolute or relative path
                    final String target = f.getLink();
                    if(StringUtils.isBlank(target)) {
                        log.warn(String.format("Missing symbolic link target for %s", parsed));
                        final EnumSet<Path.Type> type = parsed.getType();
                        type.remove(Path.Type.symboliclink);
                    }
                    else if(StringUtils.startsWith(target, String.valueOf(Path.DELIMITER))) {
                        parsed.setSymlinkTarget(new Path(PathNormalizer.normalize(target), EnumSet.of(Path.Type.file)));
                    }
                    else if(StringUtils.equals("..", target)) {
                        parsed.setSymlinkTarget(directory);
                    }
                    else if(StringUtils.equals(".", target)) {
                        parsed.setSymlinkTarget(parsed);
                    }
                    else {
                        parsed.setSymlinkTarget(new Path(directory, target, EnumSet.of(Path.Type.file)));
                    }
                    break;
            }
            if(parsed.isFile()) {
                parsed.attributes().setSize(f.getSize());
            }
            parsed.attributes().setOwner(f.getUser());
            parsed.attributes().setGroup(f.getGroup());
            Permission.Action u = Permission.Action.none;
            if(f.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION)) {
                u = u.or(Permission.Action.read);
            }
            if(f.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION)) {
                u = u.or(Permission.Action.write);
            }
            if(f.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION)) {
                u = u.or(Permission.Action.execute);
            }
            Permission.Action g = Permission.Action.none;
            if(f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION)) {
                g = g.or(Permission.Action.read);
            }
            if(f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION)) {
                g = g.or(Permission.Action.write);
            }
            if(f.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION)) {
                g = g.or(Permission.Action.execute);
            }
            Permission.Action o = Permission.Action.none;
            if(f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION)) {
                o = o.or(Permission.Action.read);
            }
            if(f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION)) {
                o = o.or(Permission.Action.write);
            }
            if(f.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION)) {
                o = o.or(Permission.Action.execute);
            }
            final Permission permission = new Permission(u, g, o);
            if(f instanceof FTPExtendedFile) {
                permission.setSetuid(((FTPExtendedFile) f).isSetuid());
                permission.setSetgid(((FTPExtendedFile) f).isSetgid());
                permission.setSticky(((FTPExtendedFile) f).isSticky());
            }
            if(!Permission.EMPTY.equals(permission)) {
                parsed.attributes().setPermission(permission);
            }
            final Calendar timestamp = f.getTimestamp();
            if(timestamp != null) {
                parsed.attributes().setModificationDate(timestamp.getTimeInMillis());
            }
            children.add(parsed);
        }
        if(!success) {
            throw new FTPInvalidListException(children);
        }
        return children;
    }
@Test(expected = ListCanceledException.class)
    @Ignore
    public void testLimit() throws Exception {
        final CompositeFileEntryParser parser = new FTPParserSelector().getParser("NETWARE  Type : L8");
        final AttributedList<Path> list = new FTPListResponseReader(parser).read(
            new Path("/", EnumSet.of(Path.Type.directory)), Collections.singletonList(
                        "lrwxrwxrwx    1 ftp      ftp            23 Feb 05 06:51 debian -> ../pool/4/mirror/debian")
        );
    }