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

sdmxdl.provider.ri.drivers.RngRiDriver Maven / Gradle / Ivy

The newest version!
package sdmxdl.provider.ri.drivers;

import lombok.NonNull;
import nbbrd.design.DirectImpl;
import nbbrd.design.RepresentableAs;
import nbbrd.design.StaticFactoryMethod;
import nbbrd.io.text.BooleanProperty;
import nbbrd.io.text.Parser;
import nbbrd.service.ServiceProvider;
import sdmxdl.*;
import sdmxdl.format.design.PropertyDefinition;
import sdmxdl.provider.ConnectionSupport;
import sdmxdl.provider.HasMarker;
import sdmxdl.provider.Marker;
import sdmxdl.provider.web.DriverSupport;
import sdmxdl.web.WebSource;
import sdmxdl.web.spi.Driver;
import sdmxdl.web.spi.WebContext;

import java.io.IOException;
import java.net.URI;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static java.util.Collections.singleton;

@DirectImpl
@ServiceProvider
public final class RngRiDriver implements Driver {

    private static final String RI_RNG = "RI_RNG";

    @PropertyDefinition
    private static final BooleanProperty ENABLE_PROPERTY =
            BooleanProperty.of("enableRngDriver", false);

    @lombok.experimental.Delegate
    private final DriverSupport support = DriverSupport
            .builder()
            .id(RI_RNG)
            .rank(NATIVE_DRIVER_RANK)
            .availability(ENABLE_PROPERTY::get)
            .connector(RngRiDriver::newConnection)
            .source(WebSource
                    .builder()
                    .id("RNG")
                    .name("en", "Random number generator")
                    .driver(RI_RNG)
                    .endpointOf("rng:3:4:0:2010-01-01")
                    .build())
            .build();

    private static @NonNull Connection newConnection(@NonNull WebSource source, @NonNull Languages languages, @NonNull WebContext context) {
        RngDriverId config = RngDriverId.parse(source.getEndpoint());

        return new RngConnection(HasMarker.of(source), config);
    }

    @RepresentableAs(URI.class)
    @lombok.Value
    @lombok.Builder
    private static class RngDriverId {

        int seriesCount;
        int yearCount;
        int seed;
        LocalDateTime start;

        public URI toURI() {
            return URI.create("rng" + ":" + seriesCount + ":" + yearCount + ":" + seed + ":" + start.toLocalDate().toString());
        }

        @StaticFactoryMethod
        public static RngDriverId parse(URI endpoint) throws IllegalArgumentException {
            String[] tmp = endpoint.toString().split(":", -1);

            if (tmp.length != 5) {
                throw new IllegalArgumentException("Invalid uri");
            }

            return new RngDriverId(
                    INTEGER_PARSER.parseValue(tmp[1]).orElse(3),
                    INTEGER_PARSER.parseValue(tmp[2]).orElse(4),
                    INTEGER_PARSER.parseValue(tmp[3]).orElse(0),
                    LOCAL_DATE_PARSER.parseValue(tmp[4]).map(LocalDate::atStartOfDay).orElse(LocalDate.of(2010, Month.JANUARY, 1).atStartOfDay())
            );
        }

        private static final Parser INTEGER_PARSER = Parser.onInteger();
        private static final Parser LOCAL_DATE_PARSER = Parser.onDateTimeFormatter(DateTimeFormatter.ISO_DATE, LocalDate::from);
    }

    @lombok.AllArgsConstructor
    private static final class RngConnection implements Connection, HasMarker {

        private static final String FREQ = "FREQ";
        private static final String INDEX = "INDEX";

        @lombok.Getter
        private final Marker marker;
        private final RngDriverId config;

        @Override
        public void testConnection() {
        }

        @Override
        public @NonNull Collection getFlows() {
            return singleton(Flow.builder().ref(FlowRef.parse("RNG")).structureRef(StructureRef.parse("STRUCT_RNG")).name("RNG").build());
        }

        @Override
        public @NonNull Flow getFlow(@NonNull FlowRef flowRef) throws IOException {
            return ConnectionSupport.getFlowFromFlows(flowRef, this, this);
        }

        @Override
        public @NonNull Structure getStructure(@NonNull FlowRef flowRef) throws IOException {
            Flow flow = getFlow(flowRef);
            return Structure
                    .builder()
                    .ref(flow.getStructureRef())
                    .dimension(Dimension
                            .builder()
                            .id(FREQ)
                            .name("Frequency")
                            .position(1)
                            .codelist(Codelist
                                    .builder()
                                    .ref(CodelistRef.parse("CL_FREQ"))
                                    .codes(Freq.stream().collect(Collectors.toMap(Freq::name, Freq::getLabel)))
                                    .build())
                            .build())
                    .dimension(Dimension
                            .builder()
                            .id(INDEX)
                            .name("Index")
                            .position(2)
                            .codelist(Codelist
                                    .builder()
                                    .ref(CodelistRef.parse("CL_INDEX"))
                                    .codes(IntStream
                                            .range(0, config.getSeriesCount())
                                            .mapToObj(String::valueOf)
                                            .collect(Collectors.toMap(series -> series, series -> "S" + series)))
                                    .build())
                            .build())
                    .timeDimensionId("TIME_PERIOD")
                    .primaryMeasureId("OBS_VALUE")
                    .name("RNG")
                    .build();
        }

        @Override
        public @NonNull DataSet getData(@NonNull FlowRef flowRef, @NonNull Query query) throws IOException {
            return ConnectionSupport.getDataSetFromStream(flowRef, query, this);
        }

        @Override
        public @NonNull Stream getDataStream(@NonNull FlowRef flowRef, @NonNull Query query) {
            return Freq.stream().flatMap(freq -> newSeriesStream(freq, query));
        }

        private Stream newSeriesStream(Freq freq, Query query) {
            return IntStream
                    .range(0, config.getSeriesCount())
                    .mapToObj(series -> Key.of(freq.name(), String.valueOf(series)))
                    .filter(query.getKey()::contains)
                    .map(key -> newSeries(key, freq, query.getDetail()));
        }

        private Series newSeries(Key key, Freq freq, Detail detail) {
            Series.Builder result = Series.builder().key(key);
            if (!detail.isIgnoreData()) {
                int series = Integer.parseInt(key.get(1));
                int obsCount = (int) freq.getUnit().between(config.getStart(), config.getStart().plusYears(config.getYearCount()));
                long startTimeMillis = config.getStart().toInstant(ZoneOffset.UTC).toEpochMilli();
                Random random = new Random(config.getSeed());
                IntStream
                        .range(0, obsCount)
                        .mapToObj(j -> Obs.builder().period(TimeInterval.of(config.getStart().plus(j, freq.getUnit()), Duration.ZERO)).value(getValue(series, startTimeMillis, random)).build())
                        .forEach(result::obs);
            }
            return result.build();
        }

        @Override
        public @NonNull Set getSupportedFeatures() {
            return EnumSet.allOf(Feature.class);
        }

        @Override
        public void close() {
        }

        private double getValue(int series, long startTimeMillis, Random random) {
            return (Math.abs((100 * (Math.cos(startTimeMillis * series))) + (100 * (Math.sin(startTimeMillis) - Math.cos(random.nextDouble()) + Math.tan(random.nextDouble())))) - 50);
        }

        @lombok.AllArgsConstructor
        @lombok.Getter
        enum Freq {
            A("Annual", ChronoUnit.YEARS),
            M("Monthly", ChronoUnit.MONTHS),
            D("Daily", ChronoUnit.DAYS);

            private final String label;
            private final ChronoUnit unit;

            public static Stream stream() {
                return Stream.of(values());
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy