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