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

io.virtdata.docsys.metafs.fs.renderfs.api.FileRenderer Maven / Gradle / Ivy

package io.virtdata.docsys.metafs.fs.renderfs.api;

import io.virtdata.docsys.metafs.fs.renderfs.model.TargetPathView;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.AccessMode;
import java.nio.file.Path;
import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@SuppressWarnings("Duplicates")
public class FileRenderer implements FileContentRenderer {

    private final String sourceExtension;
    private final String targetExtension;
    private final Pattern sourceNamePattern;
    private final Pattern targetNamePattern;
    private final boolean isCaseSensitive;

    private final ConcurrentHashMap renderables = new ConcurrentHashMap<>();
    private TemplateCompiler[] compilers;

    /**
     * Create a file renderer from a source extension to a target extension, which will yield the
     * virtual contents of the target file by applying a set of renderers to the source file data.
     *
     * @param sourceExtension The extension of the source (actual) file, including the dot and extension name.
     * @param targetExtension The extension of the target (virtual) file, including the dot and extension name.
     * @param isCaseSensitive Whether or not to do case-sensitive matching against the source and target extensions.
     * @param compilers       A lookup function which can create a renderer for a specific path as needed.
     */
    public FileRenderer(String sourceExtension, String targetExtension, boolean isCaseSensitive, TemplateCompiler... compilers) {
        this.compilers = compilers;

        if (!sourceExtension.startsWith(".")) {
            throw new InvalidParameterException("You must provide a source extension in '.xyz' form.");
        }
        if (!targetExtension.startsWith(".")) {
            throw new InvalidParameterException("You must provide a target extension in '.xyz' form.");
        }
        this.isCaseSensitive = isCaseSensitive;
        this.sourceExtension = sourceExtension;
        this.sourceNamePattern = toNamePattern(sourceExtension);
        this.targetExtension = targetExtension;
        this.targetNamePattern = toNamePattern(targetExtension);

    }

    private Pattern toNamePattern(String fileExtension) {
        Pattern.compile(fileExtension);
        if (fileExtension.matches("\\.[a-zA-Z0-9_-]+")) {
            StringBuilder sb = new StringBuilder("(?.+)(?");
            if (isCaseSensitive) {
                sb.append(fileExtension.substring(1));
            } else {
                sb.append("(");
                for (int i = 0; i < fileExtension.length(); i++) {
                    String charString = fileExtension.substring(i, i + 1);
                    if (charString.equals(".")) {
                        sb.append("\\.");
                    } else {
                        sb.append("[").append(charString.toUpperCase()).append(charString.toLowerCase()).append("]");
                    }
                }
                sb.append(")");
            }
            sb.append(")");
            return Pattern.compile(sb.toString());
        } else {
            throw new RuntimeException("Invalid extension pattern '" + fileExtension + "'. This must be all letters or numbers.");
        }
    }

    @Override
    public Pattern getSourcePattern() {
        return sourceNamePattern;
    }

    @Override
    public Pattern getTargetPattern() {
        return targetNamePattern;
    }

    public String getSourceExtension() {
        return sourceExtension;
    }

    public String getTargetExtension() {
        return targetExtension;
    }

    @Override
    public Path getSourcePath(Path targetName) {
        Matcher matcher = targetNamePattern.matcher(targetName.toString());
        if (matcher.matches()) {
            String basepath = matcher.group("basepath");
            String extension = matcher.group("extension");
            if (basepath == null || extension == null) {
                throw new RuntimeException(
                        "Unable to extract named fields 'basepath' or 'extension' from target " +
                                "name '" + targetName + "' with pattern '" + targetNamePattern + "'");
            }
            return targetName.getFileSystem().getPath(basepath + sourceExtension);

        }
        return null;

    }

    @Override
    public Path getRenderedTargetName(Path sourceName) {
        Matcher matcher = sourceNamePattern.matcher(sourceName.toString());
        if (matcher.matches()) {
            String basepath = matcher.group("basepath");
            String extension = matcher.group("extension");
            if (basepath == null || extension == null) {
                throw new RuntimeException(
                        "Unable to extract named fields 'basepath' or 'extension' from source " +
                                "name '" + sourceName + "' with pattern '" + sourceNamePattern + "'");
            }

            return sourceName.getFileSystem().getPath(basepath + targetExtension);
        }
        throw new RuntimeException("Unable to match source name '" + sourceName + "' with pattern '" + sourceNamePattern + "'");

    }

    @Override
    public String getTargetSuffix() {
        return this.targetExtension;
    }

    @Override
    public synchronized ByteBuffer render(Path sourcePath, Path targetPath, ByteBuffer byteBuffer) {
        long lastModified = RendererIO.mtimeFor(sourcePath);

        LinkedList renderLayers = getRenderLayers(sourcePath);
        Supplier compositeTemplateProvider = new CompositeTemplate(renderLayers);

        Renderable renderable = renderables.get(targetPath.toString());
        if (renderable == null) {
            if (compilers.length==1) {
                renderable = new RenderableEntry(() -> RendererIO.readBuffer(sourcePath), compilers[0]);
            } else {
                renderable = new RenderableChain(() -> RendererIO.readBuffer(sourcePath), compilers);
            }
            renderables.put(targetPath.toString(), renderable);
        }
        ByteBuffer rendered = renderable.apply(new TargetPathView(targetPath, lastModified));
        return rendered.asReadOnlyBuffer();
    }

    private LinkedList getRenderLayers(Path sourcePath) {
        sourcePath = sourcePath.normalize();
        LinkedList renderchain = new LinkedList<>();
        renderchain.add(sourcePath);
        Path directoryPath = sourcePath.getParent();

        while (directoryPath != null) {
            try {
                Path candidate = directoryPath.resolve("_template" + sourceExtension);
                candidate.getFileSystem().provider().checkAccess(candidate, AccessMode.READ);
                renderchain.addFirst(candidate);
                directoryPath = directoryPath.getParent();
            } catch (IOException e) {
                break;
            }
        }
        return renderchain;
    }

    @Override
    public String toString() {
        return this.sourceExtension + "->" + this.targetExtension + ", with " +
                Arrays.toString(this.compilers);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy