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