    001/*****************************************************************************
    002 * Copyright by The HDF Group.                                               *
    003 * All rights reserved.                                                      *
    004 *                                                                           *
    005 * This file is part of the HDF Java Products distribution.                  *
    006 * The full copyright notice, including terms governing use, modification,   *
    007 * and redistribution, is contained in the files COPYING and Copyright.html. *
    008 * COPYING can be found at the root of the source code distribution tree.    *
    009 * Or, see https://support.hdfgroup.org/products/licenses.html               *
    010 * If you do not have access to either file, you may request a copy from     *
    011 * help@hdfgroup.org.                                                        *
    012 ****************************************************************************/
    013
    014package hdf.view.TableView;
    015
    016import java.util.ArrayList;
    017import java.util.Arrays;
    018import java.util.HashMap;
    019import java.util.Iterator;
    020import java.util.List;
    021
    022import hdf.object.CompoundDataFormat;
    023import hdf.object.Datatype;
    024
    025/**
    026 * A class containing utility functions for the various DataXXXFactory classes,
    027 * such as DataProviderFactory, DataDisplayConverterFactory and
    028 * DataValidatorFactory.
    029 *
    030 * @author Jordan T. Henderson
    031 * @version 1.0 2/21/2019
    032 *
    033 */
    034public class DataFactoryUtils {
    035
    036    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DataFactoryUtils.class);
    037
    038    public static final String errStr = "*ERROR*";
    039    public static final String nullStr = "Null";
    040
    041    public static final int COL_TO_BASE_CLASS_MAP_INDEX = 0;
    042    public static final int CMPD_START_IDX_MAP_INDEX = 1;
    043
    044    /*
    045     * Given a CompoundDataFormat, as well as a compound datatype, removes the
    046     * non-selected datatypes from the List of datatypes inside the compound
    047     * datatype and returns that as a new List.
    048     */
    049    public static List<Datatype> filterNonSelectedMembers(CompoundDataFormat dataFormat, final Datatype compoundType) {
    050        List<Datatype> allSelectedTypes = Arrays.asList(dataFormat.getSelectedMemberTypes());
    051        if (allSelectedTypes == null) {
    052            log.debug("filterNonSelectedMembers(): selected compound member datatype list is null");
    053            return null;
    054        }
    055
    056        /*
    057         * Make sure to make a copy of the compound datatype's member list, as we will
    058         * make modifications to the list when members aren't selected.
    059         */
    060        List<Datatype> selectedTypes = new ArrayList<>(compoundType.getCompoundMemberTypes());
    061
    062        /*
    063         * Among the datatypes within this compound type, only keep the ones that are
    064         * actually selected in the dataset.
    065         */
    066        Iterator<Datatype> localIt = selectedTypes.iterator();
    067        while (localIt.hasNext()) {
    068            Datatype curType = localIt.next();
    069
    070            /*
    071             * Since the passed in allSelectedMembers list is a flattened out datatype
    072             * structure, we want to leave the nested compound Datatypes inside our local
    073             * list of datatypes.
    074             */
    075            if (curType.isCompound())
    076                continue;
    077
    078            if (!allSelectedTypes.contains(curType))
    079                localIt.remove();
    080        }
    081
    082        return selectedTypes;
    083    }
    084
    085    /*
    086     * TODO: can potentially merge the two functions.
    087     */
    088    @SuppressWarnings("unchecked")
    089    public static HashMap<Integer, Integer>[] buildIndexMaps(CompoundDataFormat dataFormat, List<Datatype> localSelectedTypes) throws Exception {
    090        HashMap<Integer, Integer>[] maps = new HashMap[2];
    091        maps[COL_TO_BASE_CLASS_MAP_INDEX] = new HashMap<>();
    092        maps[CMPD_START_IDX_MAP_INDEX] = new HashMap<>();
    093
    094        buildColIdxToProviderMap(maps[COL_TO_BASE_CLASS_MAP_INDEX], dataFormat, localSelectedTypes, new int[] { 0 }, new int[] { 0 }, 0);
    095        buildRelColIdxToStartIdxMap(maps[CMPD_START_IDX_MAP_INDEX], dataFormat, localSelectedTypes, new int[] { 0 }, new int[] { 0 }, 0);
    096
    097        return maps;
    098    }
    099
    100    /*
    101     * Recursive routine to build a mapping between physical column indices and
    102     * indices into the base HDFDataProvider array. For example, consider the
    103     * following compound datatype:
    104     *
    105     *  ___________________________________
    106     * |             Compound              |
    107     * |___________________________________|
    108     * |     |     |    Compound     |     |
    109     * | int | int |_________________| int |
    110     * |     |     | int | int | int |     |
    111     * |_____|_____|_____|_____|_____|_____|
    112     *
    113     * The CompoundDataProvider would have 4 base HDFDataProviders:
    114     *
    115     * [NumericalDataProvider, NumericalDataProvider, CompoundDataProvider, NumericalDataProvider]
    116     *
    117     * and the mapping between physical column indices and this array would be:
    118     *
    119     * (0=0, 1=1, 2=2, 3=2, 4=2, 5=3)
    120     *
    121     * For the nested CompoundDataProvider, the mapping would simply be:
    122     *
    123     * (0=0, 1=1, 2=2)
    124     */
    125    private static void buildColIdxToProviderMap(HashMap<Integer, Integer> outMap, CompoundDataFormat dataFormat,
    126            List<Datatype> localSelectedTypes, int[] curMapIndex, int[] curProviderIndex, int depth) throws Exception {
    127        for (int i = 0; i < localSelectedTypes.size(); i++) {
    128            Datatype curType = localSelectedTypes.get(i);
    129            log.trace("buildColIdxToStartIdxMap(): curType[{}]={}", i, curType);
    130            Datatype nestedCompoundType = null;
    131            int arrSize = 1;
    132
    133            if (curType.isArray()) {
    134                long[] arrayDims = curType.getArrayDims();
    135                for (int j = 0; j < arrayDims.length; j++) {
    136                    arrSize *= arrayDims[j];
    137                }
    138                log.trace("buildColIdxToStartIdxMap(): arrSize={}", arrSize);
    139
    140                /*
    141                 * Recursively detect any nested array/vlen of compound types.
    142                 */
    143                Datatype base = curType.getDatatypeBase();
    144                while (base != null) {
    145                    if (base.isCompound()) {
    146                        nestedCompoundType = base;
    147                        break;
    148                    }
    149                    else if (base.isArray()) {
    150                        arrayDims = base.getArrayDims();
    151                        for (int j = 0; j < arrayDims.length; j++) {
    152                            arrSize *= arrayDims[j];
    153                        }
    154                    }
    155
    156                    base = base.getDatatypeBase();
    157                }
    158                log.trace("buildColIdxToStartIdxMap(): arrSize after base={}", arrSize);
    159            }
    160
    161            if (nestedCompoundType != null) {
    162                List<Datatype> cmpdSelectedTypes = filterNonSelectedMembers(dataFormat, nestedCompoundType);
    163
    164                /*
    165                 * For Array/Vlen of Compound types, we repeat the compound members n times,
    166                 * where n is the number of array elements of variable-length elements.
    167                 * Therefore, we repeat our mapping for these types n times.
    168                 */
    169                for (int j = 0; j < arrSize; j++) {
    170                    buildColIdxToProviderMap(outMap, dataFormat, cmpdSelectedTypes, curMapIndex, curProviderIndex, depth + 1);
    171                }
    172            }
    173            else if (curType.isCompound()) {
    174                List<Datatype> cmpdSelectedTypes = filterNonSelectedMembers(dataFormat, curType);
    175
    176                buildColIdxToProviderMap(outMap, dataFormat, cmpdSelectedTypes, curMapIndex, curProviderIndex, depth + 1);
    177            }
    178            else
    179                outMap.put(curMapIndex[0]++, curProviderIndex[0]);
    180
    181            if (depth == 0)
    182                curProviderIndex[0]++;
    183        }
    184    }
    185
    186    /*
    187     * Recursive routine to build a mapping between relative indices in a compound
    188     * type and the relative index of the first member of the nested compound that
    189     * index belongs to. For example, consider the following compound datatype:
    190     *
    191     *  ___________________________________________________________
    192     * |                         Compound                          |
    193     * |___________________________________________________________|
    194     * |   Compound   |   Compound   |   Compound   |   Compound   |
    195     * |______________|______________|______________|______________|
    196     * | int |  float | int |  float | int |  float | int |  float |
    197     * |_____|________|_____|________|_____|________|_____|________|
    198     *
    199     * The top-level mapping between relative compound offsets and the relative
    200     * index of the first member of the nested compound would look like:
    201     *
    202     * (0=0, 1=0, 2=2, 3=2, 4=4, 5=4, 6=6, 7=6)
    203     *
    204     * Each of the nested Compound types would have the same mapping of:
    205     *
    206     * (0=0, 1=1)
    207     *
    208     * As the above mapping for the nested Compound types shows, when the member
    209     * in question is not part of a further nested compound, its index is simply its
    210     * offset, as in the following compound type:
    211     *
    212     *  ____________________________
    213     * |          Compound          |
    214     * |____________________________|
    215     * |     |       |   Compound   |
    216     * | int | float |______________|
    217     * |     |       | int | float  |
    218     * |_____|_______|_____|________|
    219     *
    220     * The top-level mapping would be:
    221     *
    222     * (0=0, 1=1, 2=2, 3=2)
    223     *
    224     * and the mapping for the nested compound would be:
    225     *
    226     * (0=0, 1=1)
    227     */
    228    private static void buildRelColIdxToStartIdxMap(HashMap<Integer, Integer> outMap, CompoundDataFormat dataFormat,
    229            List<Datatype> localSelectedTypes, int[] curMapIndex, int[] curStartIdx, int depth) throws Exception {
    230        for (int i = 0; i < localSelectedTypes.size(); i++) {
    231            Datatype curType = localSelectedTypes.get(i);
    232            log.trace("buildRelColIdxToStartIdxMap(): curType[{}]={}", i, curType);
    233            Datatype nestedCompoundType = null;
    234            int arrSize = 1;
    235
    236            if (curType.isArray()) {
    237                long[] arrayDims = curType.getArrayDims();
    238                for (int j = 0; j < arrayDims.length; j++) {
    239                    arrSize *= arrayDims[j];
    240                }
    241                log.trace("buildRelColIdxToStartIdxMap(): arrSize={}", arrSize);
    242
    243                /*
    244                 * Recursively detect any nested array/vlen of compound types.
    245                 */
    246                Datatype base = curType.getDatatypeBase();
    247                while (base != null) {
    248                    if (base.isCompound()) {
    249                        nestedCompoundType = base;
    250                        break;
    251                    }
    252                    else if (base.isArray()) {
    253                        arrayDims = base.getArrayDims();
    254                        for (int j = 0; j < arrayDims.length; j++) {
    255                            arrSize *= arrayDims[j];
    256                        }
    257                    }
    258
    259                    base = base.getDatatypeBase();
    260                }
    261                log.trace("buildRelColIdxToStartIdxMap(): arrSize after base={}", arrSize);
    262            }
    263
    264            if (nestedCompoundType != null) {
    265                List<Datatype> cmpdSelectedTypes = filterNonSelectedMembers(dataFormat, nestedCompoundType);
    266
    267                /*
    268                 * For Array/Vlen of Compound types, we repeat the compound members n times,
    269                 * where n is the number of array elements of variable-length elements.
    270                 * Therefore, we repeat our mapping for these types n times.
    271                 */
    272                for (int j = 0; j < arrSize; j++) {
    273                    if (depth == 0)
    274                        curStartIdx[0] = curMapIndex[0];
    275
    276                    buildRelColIdxToStartIdxMap(outMap, dataFormat, cmpdSelectedTypes, curMapIndex, curStartIdx, depth + 1);
    277                }
    278            }
    279            else if (curType.isCompound()) {
    280                if (depth == 0)
    281                    curStartIdx[0] = curMapIndex[0];
    282
    283                List<Datatype> cmpdSelectedTypes = filterNonSelectedMembers(dataFormat, curType);
    284
    285                buildRelColIdxToStartIdxMap(outMap, dataFormat, cmpdSelectedTypes, curMapIndex, curStartIdx, depth + 1);
    286            }
    287            else {
    288                if (depth == 0) {
    289                    outMap.put(curMapIndex[0], curMapIndex[0]);
    290                    curMapIndex[0]++;
    291                }
    292                else
    293                    outMap.put(curMapIndex[0]++, curStartIdx[0]);
    294            }
    295        }
    296    }
    297
    298}