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