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