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}