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

com.artipie.asto.Key Maven / Gradle / Ivy

/*
 * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
 * https://github.com/artipie/artipie/blob/master/LICENSE.txt
 */
package com.artipie.asto;

import com.artipie.ArtipieException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Storage key.
 *
 * @since 0.6
 */
public interface Key {
    /**
     * Delimiter used to split string into parts and join parts into string.
     */
    String DELIMITER = "/";

    /**
     * Comparator for key values by their string representation.
     */
    Comparator CMP_STRING = Comparator.comparing(Key::string);

    /**
     * Root key.
     */
    Key ROOT = new Key.From(Collections.emptyList());

    /**
     * Key.
     * @return Key string
     */
    String string();

    /**
     * Parent key.
     * @return Parent key or Optional.empty if the current key is ROOT
     */
    Optional parent();

    /**
     * Parts of key.
     * @return List of parts
     */
    List parts();

    /**
     * Base key class.
     * @since 1.14.0
     */
    abstract class Base implements Key {
        @Override
        public boolean equals(final Object another) {
            boolean res;
            if (this == another) {
                res = true;
            } else if (another instanceof Key from) {
                res = Objects.equals(this.parts(), from.parts());
            } else {
                res = false;
            }
            return res;
        }

        @Override
        public int hashCode() {
            return Objects.hash(this.parts());
        }

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

    /**
     * Default decorator.
     * @since 0.7
     */
    abstract class Wrap extends Base {

        /**
         * Origin key.
         */
        private final Key origin;

        /**
         * Ctor.
         * @param origin Origin key
         */
        protected Wrap(final Key origin) {
            this.origin = origin;
        }

        @Override
        public final String string() {
            return this.origin.string();
        }

        @Override
        public Optional parent() {
            return this.origin.parent();
        }

        @Override
        public List parts() {
            return this.origin.parts();
        }

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

        @Override
        public boolean equals(final Object another) {
            return this.origin.equals(another);
        }

        @Override
        public int hashCode() {
            return this.origin.hashCode();
        }
    }

    /**
     * Key from something.
     * @since 0.6
     */
    final class From extends Base {

        /**
         * Parts.
         */
        private final List parts;

        /**
         * Ctor.
         * @param parts Parts delimited by `/` symbol
         */
        public From(final String parts) {
            this(parts.split(Key.DELIMITER));
        }

        /**
         * Ctor.
         * @param parts Parts
         */
        public From(final String... parts) {
            this(Arrays.asList(parts));
        }

        /**
         * Key from two keys.
         * @param first First key
         * @param second Second key
         */
        public From(final Key first, final Key second) {
            this(
                Stream.concat(
                    new From(first).parts.stream(),
                    new From(second).parts.stream()
                ).collect(Collectors.toList())
            );
        }

        /**
         * From base path and parts.
         * @param base Base path
         * @param parts Parts
         */
        public From(final Key base, final String... parts) {
            this(
                Stream.concat(
                    new From(base.string()).parts.stream(),
                    Arrays.stream(parts)
                ).collect(Collectors.toList())
            );
        }

        /**
         * Ctor.
         * @param parts Parts
         */
        @SuppressWarnings("PMD.ConstructorOnlyInitializesOrCallOtherConstructors")
        public From(final List parts) {
            if (parts.size() == 1 && parts.get(0).isEmpty()) {
                this.parts = Collections.emptyList();
            } else {
                this.parts = parts.stream()
                    .flatMap(part -> Arrays.stream(part.split("/")))
                    .collect(Collectors.toList());
            }
        }

        @Override
        public String string() {
            for (final String part : this.parts) {
                if (part.isEmpty()) {
                    throw new ArtipieException("Empty parts are not allowed");
                }
                if (part.contains(Key.DELIMITER)) {
                    throw new ArtipieException(String.format("Invalid part: '%s'", part));
                }
            }
            return String.join(Key.DELIMITER, this.parts);
        }

        @Override
        public Optional parent() {
            final Optional parent;
            if (this.parts.isEmpty()) {
                parent = Optional.empty();
            } else {
                parent = Optional.of(
                    new Key.From(this.parts.subList(0, this.parts.size() - 1))
                );
            }
            return parent;
        }

        @Override
        public List parts() {
            return Collections.unmodifiableList(this.parts);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy