All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aksw.commons.io.util.SymLinkUtils Maven / Gradle / Ivy

package org.aksw.commons.io.util;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.aksw.commons.io.util.symlink.SymbolicLinkStrategy;
import org.aksw.commons.lambda.throwing.ThrowingBiFunction;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.google.common.collect.Table.Cell;
import com.google.common.collect.Tables;

public class SymLinkUtils {
    /**
     * Read a symbolic link and return and absolute path to its target.
     *
     * @param symLink
     * @return
     * @throws IOException
     */
    public static Path readSymLinkAbsolute(Path symLink) throws IOException {
        if (!Files.isSymbolicLink(symLink)) {
            throw new IllegalArgumentException("Not a symbolic link: " + symLink);
        }
        Path symLinkTgt = Files.readSymbolicLink(symLink);

        Path result = resolveSymLinkAbsolute(symLink, symLinkTgt);
        return result;
    }

    /**
     * Given a path that is considered a symlink and its target, return the absolute path
     * obtained by resolving the target (which may be a relative path) against the symlink.
     *
     * This method allows resolving a relative symlinkTgt of a different file system (e.g. UNIX) against
     * a symLinkSrc (e.g. WebDAV)
     *
     * @param symLinkSrc
     * @param symLinkTgt
     * @return
     */
    public static Path resolveSymLinkAbsolute(Path symLinkSrc, Path symLinkTgt) {
        // resolveSibling instead of getParent.resolve?

        Path result;
        if (symLinkTgt.isAbsolute()) {
            result = symLinkTgt;
        } else {
            String[] tgtSegments = PathUtils.getPathSegments(symLinkTgt);
            result = PathUtils.resolve(symLinkSrc.getParent(), tgtSegments).normalize().toAbsolutePath();
        }
        return result;
    }

    /**
     * Within 'folder' create a link to 'file' with name 'baseName' if it does not yet exist.
     * Return the new link or all prior existing link(s)
     *
     * @param file
     * @param folder
     * @param baseName
     * @return
     * @throws IOException
     */
    public static Collection allocateSymbolicLink(
            SymbolicLinkStrategy symlinkStrategy,
            Path rawTarget,
            Path rawSourceFolder,
            Function fileNameNormalizer,
            String prefix,
            String suffix
            ) throws Exception {

        return allocateSymbolicLink(symlinkStrategy, rawTarget, rawSourceFolder, fileNameNormalizer, prefix, suffix,
                (file, tgt) -> {
                    symlinkStrategy.createSymbolicLink(file, tgt);
                    return file;
                });
    }


    public static Collection allocateSymbolicLink(
            SymbolicLinkStrategy symlinkStrategy,
            Path rawTarget,
            Path rawSourceFolder,
            Function fileNameNormalizer,
            String prefix,
            String suffix,
            ThrowingBiFunction tgtAndContentToFile // Called when a new symlink is allocated
            ) throws Exception {
        Path sourceFolder = rawSourceFolder.normalize();
        Path target = rawTarget.normalize();

        Path relTgt = sourceFolder.relativize(target);

        Path absTarget = target.toAbsolutePath();
//		Path folder = rawFolder.normalize();
//		Path file = rawFile.normalize().relativize(folder);

        //System.out.println("Realtivation: " + file.relativize(folder));

        Table existingSymLinks = readSymbolicLinks(symlinkStrategy, rawSourceFolder, fileNameNormalizer, prefix, suffix);

        Collection result = existingSymLinks.cellSet().stream()
                .filter(e -> {
                    Path absCand = resolveSymLinkAbsolute(e.getColumnKey(), e.getValue()); //e.getKey().getParent().resolve(e.getValue()).normalize().toAbsolutePath();
                    boolean r = absCand.equals(absTarget);
                    return r;
                })
                .map(Cell::getColumnKey)
                .collect(Collectors.toSet());

        // Check all symlinks in the folder whether any points to target
//        Collection result = Files.list(sourceFolder)
//            .filter(Files::isSymbolicLink)
//            .filter(t -> {
//                Path tgt;
//                try {
//                     tgt = Files.readSymbolicLink(t);
//                     tgt = tgt.toAbsolutePath();
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }
//
//                boolean r = Objects.equals(absTarget, tgt);
//                return r;
//            })
//            .collect(Collectors.toList());

        if (result.isEmpty()) {
            for(int i = 1; ; ++i) {
                String cand = prefix + (i == 1 ? "" : i) + suffix;
                Path c = sourceFolder.resolve(cand);

                //Path relTgt = c.relativize(target);

                if (!Files.exists(c, LinkOption.NOFOLLOW_LINKS)) {
                    Path tmp = tgtAndContentToFile.apply(c, relTgt);
                    // symlinkStrategy.createSymbolicLink(c, relTgt);
                    // Files.createSymbolicLink(c, relTgt);
                    result = Collections.singleton(tmp);
                    break;
                }
            }
        }

        return result;
    }

    public static Function, Stream>> streamSymbolicLinks(
            SymbolicLinkStrategy symlinkStrategy,
            Function fileNameHarmonizer,
            String prefix,
            String suffix) throws IOException {

        return pathStream -> pathStream
            .filter(symlinkStrategy::isSymbolicLink)
            .flatMap(path -> {
                String rawFileName = path.getFileName().toString();
                String fileName = fileNameHarmonizer.apply(rawFileName);

                boolean isAccepted = fileName.startsWith(prefix) && fileName.endsWith(suffix);


                Stream> r;
                if (isAccepted) {
                    try {
                        Path linkTarget = symlinkStrategy.readSymbolicLink(path);
                        r = Stream.of(Tables.immutableCell(fileName, path, linkTarget));
                    } catch (Exception e) {
                        // logger.warn("Failed to read a (virtual) symlink", e);
                        r = Stream.empty();
                    }
                } else {
                    r = Stream.empty();
                }

                return r;
            });
    }


    /**
     * Within 'sourceFolder' read all symbolic links with the pattern 'baseName${number}' and return a map
     * with their targets.
     *
     * @param rawSourceFolder
     * @param baseName
     * @return
     * @throws IOException
     */
    public static Table readSymbolicLinks(
            SymbolicLinkStrategy symlinkStrategy,
            Path sourceFolder,
            Function filenameNormalizer,
            String prefix,
            String suffix) throws IOException {
        Table result = HashBasedTable.create();

        try (Stream stream = Files.list(sourceFolder)) {
            streamSymbolicLinks(symlinkStrategy, filenameNormalizer, prefix, suffix)
                .apply(stream)
                .forEach(cell -> result.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()));

        }

        return result;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy