package com.github.alkedr;

import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;

// simple:   getProperty("my.property", Long::valueOf)
// default:  getProperty("my.property", optional(Long::valueOf)).orElse(-1)
// Не использовать String.valueOf, потому что нуллы!
// TODO: пример использования с файлами (несколько источников пропертей)
// TODO: ссылки на пропертилоадер и овнер в ридми
// TODO: Pattern.compile(",")::splitAsStream in examples
public class SimplePropertyLoader {
    private static final SimplePropertyLoader PROPERTY_LOADER_FROM_SYSTEM_GET_PROPERTY = new SimplePropertyLoader(System::getProperty);

    private final Function<String, String> propertyNameToNullablePropertyValueFunction;


    public SimplePropertyLoader(Function<String, String> propertyNameToNullablePropertyValueFunction) {
        this.propertyNameToNullablePropertyValueFunction = propertyNameToNullablePropertyValueFunction;
    }


    public String getProperty(String name) {
        return optionalToRequired(name, getOptionalProperty(name));
    }

    public <T> T getProperty(String name, PropertyValueParser<T> parser) {
        return optionalToRequired(name, getOptionalProperty(name, parser));
    }

    public <T> Stream<T> getProperty(String name, PropertyValueParser<Stream<String>> splitter,
                                     PropertyValueParser<T> elementParser) {
        return optionalToRequired(name, getOptionalProperty(name, splitter, elementParser));
    }


    public Optional<String> getOptionalProperty(String name) {
        return Optional.ofNullable(propertyNameToNullablePropertyValueFunction.apply(name));
    }

    public <T> Optional<T> getOptionalProperty(String name, PropertyValueParser<T> parser) {
        return getOptionalProperty(name).map(value -> invokeParser(parser, name, value));
    }

    // Stream, возвращённый splitter'ом, не должен быть параллельным
    public <T> Optional<Stream<T>> getOptionalProperty(String name, PropertyValueParser<Stream<String>> splitter,
                                                       PropertyValueParser<T> elementParser) {
        return getOptionalProperty(name)
                .map(value -> {
                    int[] counter = {0};
                    return invokeParser(splitter, name, value)
                            .map(elementValue -> invokeParser(
                                    elementParser,
                                    String.format("%s[%d]", name, counter[0]++),
                                    elementValue
                            ));
                });
    }


    public static String getSystemProperty(String name) {
        return PROPERTY_LOADER_FROM_SYSTEM_GET_PROPERTY.getProperty(name);
    }

    public static <T> T getSystemProperty(String name, PropertyValueParser<T> parser) {
        return PROPERTY_LOADER_FROM_SYSTEM_GET_PROPERTY.getProperty(name, parser);
    }

    public static <T> Stream<T> getSystemProperty(String name, PropertyValueParser<Stream<String>> splitter,
                                                  PropertyValueParser<T> elementParser) {
        return PROPERTY_LOADER_FROM_SYSTEM_GET_PROPERTY.getProperty(name, splitter, elementParser);
    }


    public static Optional<String> getOptionalSystemProperty(String name) {
        return PROPERTY_LOADER_FROM_SYSTEM_GET_PROPERTY.getOptionalProperty(name);
    }

    public static <T> Optional<T> getOptionalSystemProperty(String name, PropertyValueParser<T> parser) {
        return PROPERTY_LOADER_FROM_SYSTEM_GET_PROPERTY.getOptionalProperty(name, parser);
    }

    public static <T> Optional<Stream<T>> getOptionalSystemProperty(String name, PropertyValueParser<Stream<String>> splitter,
                                                                    PropertyValueParser<T> elementParser) {
        return PROPERTY_LOADER_FROM_SYSTEM_GET_PROPERTY.getOptionalProperty(name, splitter, elementParser);
    }


    private static <T> T optionalToRequired(String name, Optional<T> optional) {
        return optional.orElseThrow(() -> new PropertyIsNotDefinedException(name));
    }

    private static <T> T invokeParser(PropertyValueParser<T> parser, String name, String value) {
        try {
            return parser.parse(value);
        } catch (Exception e) {
            throw new PropertyValueParsingException(name, value, e);
        }
    }


    @FunctionalInterface
    public interface PropertyValueParser<T> {
        T parse(String value) throws Exception;
    }


    public static class PropertyIsNotDefinedException extends RuntimeException {
        public PropertyIsNotDefinedException(String name) {
            super(String.format("Property '%s' is not defined", name));
        }
    }

    public static class PropertyValueParsingException extends RuntimeException {
        public PropertyValueParsingException(String name, String value, Exception e) {
            super(String.format("Can't parse value of property '%s': '%s': %s", name, value, e.getMessage()), e);
        }
    }
}

@@ -121,372 +121,8 @@
 m;%0A%0A
-// simple:   getProperty(%22my.property%22, Long::valueOf)%0A// default:  getProperty(%22my.property%22, optional(Long::valueOf)).orElse(-1)%0A// %D0%9D%D0%B5 %D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D1%8C String.valueOf, %D0%BF%D0%BE%D1%82%D0%BE%D0%BC%D1%83 %D1%87%D1%82%D0%BE %D0%BD%D1%83%D0%BB%D0%BB%D1%8B!%0A// TODO: %D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80 %D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F %D1%81 %D1%84%D0%B0%D0%B9%D0%BB%D0%B0%D0%BC%D0%B8 (%D0%BD%D0%B5%D1%81%D0%BA%D0%BE%D0%BB%D1%8C%D0%BA%D0%BE %D0%B8%D1%81%D1%82%D0%BE%D1%87%D0%BD%D0%B8%D0%BA%D0%BE%D0%B2 %D0%BF%D1%80%D0%BE%D0%BF%D0%B5%D1%80%D1%82%D0%B5%D0%B9)%0A// TODO: %D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8 %D0%BD%D0%B0 %D0%BF%D1%80%D0%BE%D0%BF%D0%B5%D1%80%D1%82%D0%B8%D0%BB%D0%BE%D0%B0%D0%B4%D0%B5%D1%80 %D0%B8 %D0%BE%D0%B2%D0%BD%D0%B5%D1%80 %D0%B2 %D1%80%D0%B8%D0%B4%D0%BC%D0%B8%0A// TODO: Pattern.compile(%22,%22)::splitAsStream in examples%0A
 publ
@@ -1505,77 +1505,8 @@
  %7D%0A%0A
-    // Stream, %D0%B2%D0%BE%D0%B7%D0%B2%D1%80%D0%B0%D1%89%D1%91%D0%BD%D0%BD%D1%8B%D0%B9 splitter'%D0%BE%D0%BC, %D0%BD%D0%B5 %D0%B4%D0%BE%D0%BB%D0%B6%D0%B5%D0%BD %D0%B1%D1%8B%D1%82%D1%8C %D0%BF%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%BC%0A
     

Remove obsolete comments