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

sdmxdl.ext.SdmxCubeUtil Maven / Gradle / Ivy

There is a newer version: 3.0.0-beta.13
Show newest version
/*
 * Copyright 2015 National Bank of Belgium
 *
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 *
 * http://ec.europa.eu/idabc/eupl
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 */
package sdmxdl.ext;

import lombok.NonNull;
import org.checkerframework.checker.index.qual.NonNegative;
import sdmxdl.*;

import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static sdmxdl.Detail.*;

/**
 * Utility class used by JDemetra+ plugin.
 *
 * @author Philippe Charles
 */
@lombok.experimental.UtilityClass
public class SdmxCubeUtil {

    public @NonNull Stream getAllSeries(@NonNull Connection conn, @NonNull FlowRef flow, @NonNull Key node) throws IOException, IllegalArgumentException {
        if (node.isSeries()) {
            throw new IllegalArgumentException("Expecting node");
        }
        return isDataQueryDetailSupported(conn)
                ? request(conn, flow, node, NO_DATA)
                : computeKeys(conn, flow, node);
    }

    public @NonNull Stream getAllSeriesWithData(@NonNull Connection conn, @NonNull FlowRef flow, @NonNull Key node) throws IOException, IllegalArgumentException {
        if (node.isSeries()) {
            throw new IllegalArgumentException("Expecting node");
        }
        return isDataQueryDetailSupported(conn)
                ? request(conn, flow, node, FULL)
                : computeKeysAndRequestData(conn, flow, node);
    }

    public @NonNull Optional getSeries(@NonNull Connection conn, @NonNull FlowRef flow, @NonNull Key leaf) throws IOException, IllegalArgumentException {
        if (!leaf.isSeries()) {
            throw new IllegalArgumentException("Expecting leaf");
        }
        try (Stream stream = request(conn, flow, leaf, NO_DATA)) {
            return stream.findFirst();
        }
    }

    public @NonNull Optional getSeriesWithData(@NonNull Connection conn, @NonNull FlowRef flow, @NonNull Key leaf) throws IOException, IllegalArgumentException {
        if (!leaf.isSeries()) {
            throw new IllegalArgumentException("Expecting leaf");
        }
        try (Stream stream = request(conn, flow, leaf, FULL)) {
            return stream.findFirst();
        }
    }

    public @NonNull Stream getChildren(@NonNull Connection conn, @NonNull FlowRef flow, @NonNull Key node, @NonNegative int dimensionIndex) throws IOException {
        if (dimensionIndex < 0) {
            throw new IllegalArgumentException("Expecting dimensionIndex >= 0");
        }
        if (node.isSeries()) {
            throw new IllegalArgumentException("Expecting node");
        }
        if (!node.equals(Key.ALL) && !node.isWildcard(dimensionIndex)) {
            throw new IllegalArgumentException("Expecting wildcard on dimensionIndex");
        }
        return isDataQueryDetailSupported(conn)
                ? request(conn, flow, node, SERIES_KEYS_ONLY).map(series -> series.getKey().get(dimensionIndex)).distinct()
                : computeAllPossibleChildren(conn.getStructure(flow).getDimensionList(), dimensionIndex);
    }

    public @NonNull Optional getDimensionById(@NonNull Structure dsd, @NonNull String id) {
        return dsd.getDimensions().stream().filter(dimension -> dimension.getId().equals(id)).findFirst();
    }

    public @NonNull OptionalInt getDimensionIndexById(@NonNull Structure dsd, @NonNull String id) {
        List dimensionList = dsd.getDimensionList();
        for (int i = 0; i < dimensionList.size(); i++) {
            if (dimensionList.get(i).getId().equals(id)) {
                return OptionalInt.of(i);
            }
        }
        return OptionalInt.empty();
    }

    private Stream request(Connection conn, FlowRef flow, Key key, Detail detail) throws IOException {
        return conn.getDataStream(flow, Query.builder().key(key).detail(detail).build());
    }

    private Stream computeKeys(Connection conn, FlowRef flow, Key key) throws IOException {
        return computeAllPossibleSeries(conn.getStructure(flow), key)
                .map(SdmxCubeUtil::emptySeriesOf);
    }

    private Stream computeKeysAndRequestData(Connection conn, FlowRef flow, Key key) throws IOException {
        Map dataByKey = dataByKey(conn, flow, key);
        return computeAllPossibleSeries(conn.getStructure(flow), key)
                .map(seriesKey -> dataByKey.computeIfAbsent(seriesKey, SdmxCubeUtil::emptySeriesOf));
    }

    private Map dataByKey(Connection conn, FlowRef flow, Key key) throws IOException {
        try (Stream cursor = request(conn, flow, key, FULL)) {
            return cursor.collect(Collectors.toMap(Series::getKey, Function.identity()));
        }
    }

    private Stream computeAllPossibleSeries(Structure dsd, Key ref) {
        return computeAllPossibleSeries(dsd.getDimensionList(), ref);
    }

    private Stream computeAllPossibleSeries(List dimensions, Key ref) {
        List result = new ArrayList<>();
        String[] stack = new String[dimensions.size()];
        computeAllPossibleSeries(index -> getCodeList(dimensions, ref, index), 0, stack, result);
        return result.stream();
    }

    private Set getCodeList(List dimensions, Key ref, int dimensionIndex) {
        return Key.ALL.equals(ref) || ref.isWildcard(dimensionIndex)
                ? dimensions.get(dimensionIndex).getCodes().keySet()
                : Collections.singleton(ref.get(dimensionIndex));
    }

    private void computeAllPossibleSeries(IntFunction> codeLists, int idx, String[] stack, List result) {
        codeLists.apply(idx).forEach(code -> {
            stack[idx] = code;
            if (idx == stack.length - 1) {
                result.add(Key.of(stack));
            } else {
                computeAllPossibleSeries(codeLists, idx + 1, stack, result);
            }
        });
    }

    private Stream computeAllPossibleChildren(List dimensions, int dimensionIndex) {
        return dimensions.get(dimensionIndex).getCodes().keySet().stream().sorted();
    }

    private Series emptySeriesOf(Key key) {
        return Series.builder().key(key).build();
    }

    private static boolean isDataQueryDetailSupported(Connection conn) throws IOException {
        return conn.getSupportedFeatures().contains(Feature.DATA_QUERY_DETAIL);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy