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}