no.ssb.jsonstat.v2.support.DatasetTableView.java Source code

Java tutorial

Introduction

Here is the source code for no.ssb.jsonstat.v2.support.DatasetTableView.java

Source

/**
 * Copyright (C) 2016 Hadrien Kohl (hadrien.kohl@gmail.com) and contributors
 *
 *     DatasetTableView.java
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package no.ssb.jsonstat.v2.support;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import com.google.common.collect.Tables;
import com.google.common.collect.UnmodifiableIterator;
import no.ssb.jsonstat.v2.Dataset;
import no.ssb.jsonstat.v2.Dimension;

import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * An implementation of a {@link Table} that uses a {@link Dataset}
 * as data source.
 */
public class DatasetTableView implements Table<List<String>, List<String>, Number> {

    private final Dataset source;

    private final ImmutableSet<String> rows;
    private final ImmutableSet<String> columns;
    private final ImmutableMap<String, Integer> factors;
    private final ImmutableMap<String, ImmutableList<String>> dimensions;

    private final Set<List<String>> rowIndex;
    private final Set<List<String>> columnIndex;

    private final Integer size;

    public DatasetTableView(Dataset dataset, Set<String> rows, Set<String> colums) {
        this.source = checkNotNull(dataset, "dataset cannot be null");

        checkArgument(source.getDimension().keySet().equals(Sets.union(rows, colums)),
                "invalid row or column dimension names");
        this.rows = ImmutableSet.copyOf(rows);
        this.columns = ImmutableSet.copyOf(colums);

        checkArgument(dataset.getId().size() == dataset.getSize().size());
        checkArgument(dataset.getId().size() >= 2, "need at least two dimensions to " + "represent as a table");

        ImmutableMap.Builder<String, Integer> factors = ImmutableMap.builder();

        UnmodifiableIterator<Integer> sizeIterator = dataset.getSize().reverse().iterator();
        UnmodifiableIterator<String> idIterator = dataset.getId().asList().reverse().iterator();

        factors.put(idIterator.next(), 1);
        //sizeIterator.next();

        Integer size = 1;
        while (sizeIterator.hasNext() && idIterator.hasNext()) {
            size *= sizeIterator.next();
            factors.put(idIterator.next(), size);
        }
        this.factors = factors.build();

        ImmutableMap.Builder<String, ImmutableList<String>> dimensions = ImmutableMap.builder();
        for (Map.Entry<String, Dimension> dimensionEntry : source.getDimension().entrySet()) {
            String dimensionName = dimensionEntry.getKey();
            ImmutableList<String> dimensionIndex = dimensionEntry.getValue().getCategory().getIndex().asList();
            dimensions.put(dimensionName, dimensionIndex);
        }
        this.dimensions = dimensions.build();

        this.rowIndex = computeIndex(rows);
        this.columnIndex = computeIndex(colums);

        this.size = source.getSize().stream().reduce(1, (a, b) -> a * b);
    }

    /**
     * Delegates to {@link Map#containsKey}. Returns {@code false} on {@code
     * ClassCastException} and {@code NullPointerException}.
     */
    static boolean safeContainsKey(Map<?, ?> map, Object key) {
        checkNotNull(map);
        try {
            return map.containsKey(key);
        } catch (ClassCastException | NullPointerException e) {
            return false;
        }
    }

    /**
     * Delegates to {@link Map#get}. Returns {@code null} on {@code
     * ClassCastException} and {@code NullPointerException}.
     */
    static <V> V safeGet(Map<?, V> map, Object key) {
        checkNotNull(map);
        try {
            return map.get(key);
        } catch (ClassCastException | NullPointerException e) {
            return null;
        }
    }

    private Set<List<String>> computeIndex(Set<String> dimensions) {
        List<Set<String>> rowDimensions = Lists.newArrayList();
        for (String row : dimensions) {
            rowDimensions.add(ImmutableSet.copyOf(this.dimensions.get(row)));
        }
        return Sets.cartesianProduct(rowDimensions);
    }

    @Override
    public boolean containsRow(Object rowKey) {
        return safeContainsKey(rowMap(), rowKey);
    }

    @Override
    public boolean containsColumn(Object columnKey) {
        return safeContainsKey(columnMap(), columnKey);
    }

    @Override
    public Set<List<String>> rowKeySet() {
        return rowIndex;
    }

    @Override
    public Set<List<String>> columnKeySet() {
        return columnIndex;
    }

    @Override
    public boolean containsValue(Object value) {
        for (Map<List<String>, Number> row : rowMap().values()) {
            if (row.containsValue(value)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean contains(Object rowKey, Object columnKey) {
        return get(rowKey, columnKey) != null;
    }

    @Override
    public Number get(Object rowKey, Object columnKey) {
        try {
            Iterator<String> rowList = ((List<String>) rowKey).iterator();
            Iterator<String> columnList = ((List<String>) columnKey).iterator();
            int index = 0;
            for (String row : rows) {
                String key = rowList.next();
                index += dimensions.get(row).indexOf(key) * factors.get(row);
            }
            for (String column : columns) {
                String key = columnList.next();
                index += dimensions.get(column).indexOf(key) * factors.get(column);
            }
            return source.getValue().get(index);
        } catch (ClassCastException e) {
            return null;
        }
    }

    @Override
    public boolean isEmpty() {
        return size() == 0;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public Map<List<String>, Number> row(List<String> rowKey) {
        return new AbstractMap<List<String>, Number>() {

            @Override
            public Set<Entry<List<String>, Number>> entrySet() {
                return new AbstractSet<Entry<List<String>, Number>>() {
                    @Override
                    public Iterator<Entry<List<String>, Number>> iterator() {
                        return Iterators.transform(DatasetTableView.this.columnKeySet().iterator(), columnKey -> {
                            return new SimpleEntry<>(columnKey, DatasetTableView.this.get(rowKey, columnKey));
                        });
                    }

                    @Override
                    public int size() {
                        return DatasetTableView.this.columnKeySet().size();
                    }
                };
            }
        };
    }

    @Override
    public Map<List<String>, Number> column(List<String> columnKey) {
        return new AbstractMap<List<String>, Number>() {

            @Override
            public Set<Entry<List<String>, Number>> entrySet() {
                return new AbstractSet<Entry<List<String>, Number>>() {
                    @Override
                    public Iterator<Entry<List<String>, Number>> iterator() {
                        return Iterators.transform(DatasetTableView.this.rowKeySet().iterator(), rowKey -> {
                            return new SimpleEntry<>(rowKey, DatasetTableView.this.get(rowKey, columnKey));
                        });
                    }

                    @Override
                    public int size() {
                        return DatasetTableView.this.rowKeySet().size();
                    }
                };
            }
        };
    }

    @Override
    public Set<Cell<List<String>, List<String>, Number>> cellSet() {
        Set<List<List<String>>> lists = Sets.cartesianProduct(rowKeySet(), columnKeySet());
        return lists.stream().map(dimensions -> {
            return Tables.immutableCell(dimensions.get(0), dimensions.get(1),
                    get(dimensions.get(0), dimensions.get(1)));
        }).collect(Collectors.toSet());
    }

    @Override
    public Collection<Number> values() {
        return source.getRows();
    }

    @Override
    public Map<List<String>, Map<List<String>, Number>> rowMap() {
        return new AbstractMap<List<String>, Map<List<String>, Number>>() {
            @Override
            public Set<Entry<List<String>, Map<List<String>, Number>>> entrySet() {
                return new AbstractSet<Entry<List<String>, Map<List<String>, Number>>>() {
                    @Override
                    public Iterator<Entry<List<String>, Map<List<String>, Number>>> iterator() {
                        return Iterators.transform(rowKeySet().iterator(), rowKey -> {
                            return new SimpleEntry<>(rowKey, DatasetTableView.this.row(rowKey));
                        });
                    }

                    @Override
                    public int size() {
                        return rowKeySet().size();
                    }
                };
            }
        };
    }

    @Override
    public Map<List<String>, Map<List<String>, Number>> columnMap() {
        return new AbstractMap<List<String>, Map<List<String>, Number>>() {
            @Override
            public Set<Entry<List<String>, Map<List<String>, Number>>> entrySet() {
                return new AbstractSet<Entry<List<String>, Map<List<String>, Number>>>() {
                    @Override
                    public Iterator<Entry<List<String>, Map<List<String>, Number>>> iterator() {
                        return Iterators.transform(columnKeySet().iterator(), columnKey -> {
                            return new SimpleEntry<>(columnKey, DatasetTableView.this.column(columnKey));
                        });
                    }

                    @Override
                    public int size() {
                        return columnKeySet().size();
                    }
                };
            }
        };
    }

    /**
     * Guaranteed to throw an exception and leave the table unmodified.
     *
     * @throws UnsupportedOperationException always
     * @deprecated Unsupported operation.
     */
    @Deprecated
    @Override
    public final void clear() {
        throw new UnsupportedOperationException();
    }

    /**
     * Guaranteed to throw an exception and leave the table unmodified.
     *
     * @throws UnsupportedOperationException always
     * @deprecated Unsupported operation.
     */
    @Deprecated
    @Override
    public final Number put(List<String> rowKey, List<String> columnKey, Number value) {
        throw new UnsupportedOperationException();
    }

    /**
     * Guaranteed to throw an exception and leave the table unmodified.
     *
     * @throws UnsupportedOperationException always
     * @deprecated Unsupported operation.
     */
    @Deprecated
    @Override
    public final void putAll(Table<? extends List<String>, ? extends List<String>, ? extends Number> table) {
        throw new UnsupportedOperationException();
    }

    /**
     * Guaranteed to throw an exception and leave the table unmodified.
     *
     * @throws UnsupportedOperationException always
     * @deprecated Unsupported operation.
     */
    @Deprecated
    @Override
    public final Number remove(Object rowKey, Object columnKey) {
        throw new UnsupportedOperationException();
    }

}