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}