sdmxdl.ext.SdmxCubeUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sdmx-dl-api Show documentation
Show all versions of sdmx-dl-api Show documentation
Easily download official statistics - API
/*
* 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);
}
}