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