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}