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

io.neba.core.util.ResourcePaths Maven / Gradle / Ivy

package io.neba.core.util;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Collections.singletonList;
import static java.util.regex.Pattern.compile;

/**
 * A factory for {@link ResourcePath paths}, which are efficient immutable representations for paths of the form
 * /some/segment/${placeholder}/..., such that an effective paths can be {@link ResourcePath#resolve(Function) resolved}
 * using a callback function.
 */
public class ResourcePaths {
    private static final String MATCH_NAME = "placeholder";
    private static final Pattern PLACEHOLDER = compile("\\$\\{(?<" + MATCH_NAME + ">[^}|\\s]+)}");

    /**
     * @param path must not be null.
     * @return never null.
     */
    public static ResourcePath path(String path) {
        if (path == null) {
            throw new IllegalArgumentException("Method argument path must not be null");
        }

        if (PLACEHOLDER.matcher(path).find()) {
            return new PathWithPlaceholders(path);
        }
        return new PathWithoutPlaceholders(path);
    }

    /**
     * Represents a path consisting of segments that may represent placeholders of the form
     * ${name} which can be resolved via {@link ResourcePath#resolve(Function)}.
     *
     * @author Olaf Otto
     */
    public interface ResourcePath {
        default boolean hasPlaceholders() {
            return false;
        }

        /**
         * @param resolver must not be null.
         * @return a path with resolved segments, never null.
         */
        ResourcePath resolve(Function resolver);

        String getPath();
    }

    private static class PathWithPlaceholders implements ResourcePath {
        private final List segments;

        private PathWithPlaceholders(String path) {
            this(segments(path));
        }

        private PathWithPlaceholders(List segments) {
            this.segments = segments;
        }

        public ResourcePath resolve(Function placeholderResolver) {
            if (placeholderResolver == null) {
                throw new IllegalArgumentException("Method argument placeholderResolver must not be null");
            }

            return new PathWithoutPlaceholders(
                    this.segments
                            .stream()
                            .map(s -> s.isPlaceholder() ? new Value(placeholderResolver.apply(s.value)) : s)
                            .map(Value::getValue)
                            .reduce("", String::concat));
        }

        @Override
        public String getPath() {
            return this.segments.stream().map(Value::getValue).reduce("", String::concat);
        }

        @Override
        public String toString() {
            return getPath();
        }

        public boolean hasPlaceholders() {
            return true;
        }

        private static class Value {
            private final String value;

            private Value(String value) {
                this.value = value;
            }

            public String getValue() {
                return this.value;
            }

            public boolean isPlaceholder() {
                return false;
            }
        }

        private static class Placeholder extends Value {
            private Placeholder(String value) {
                super(value);
            }

            @Override
            public String getValue() {
                return "${" + super.getValue() + '}';
            }

            @Override
            public boolean isPlaceholder() {
                return true;
            }
        }

        private static List segments(String path) {
            final Matcher matcher = PLACEHOLDER.matcher(path);
            if (!matcher.find()) {
                return singletonList(new Value(path));
            }
            List segments = new ArrayList<>(4);
            int last = 0;
            do {
                if (matcher.start() != 0) {
                    segments.add(new Value(path.substring(last, matcher.start())));
                }
                segments.add(new Placeholder(matcher.group(MATCH_NAME)));
                last = matcher.end();
            } while (matcher.find());

            if (last != path.length()) {
                segments.add(new Value(path.substring(last)));
            }
            return segments;

        }
    }

    private static class PathWithoutPlaceholders implements ResourcePath {
        private final String path;

        private PathWithoutPlaceholders(String path) {
            this.path = path;
        }

        @Override
        public ResourcePath resolve(Function resolver) {
            return this;
        }

        @Override
        public String toString() {
            return this.path;
        }

        @Override
        public String getPath() {
            return path;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy