1+ package org .hdf5javalib .utils ;
2+
3+ import org .hdf5javalib .dataclass .HdfData ;
4+ import org .hdf5javalib .dataclass .HdfFixedPoint ;
5+ import org .hdf5javalib .datasource .TypedDataSource ;
6+ import org .hdf5javalib .hdffile .dataobjects .HdfObjectHeaderPrefix ;
7+ import org .hdf5javalib .hdffile .dataobjects .messages .AttributeMessage ;
8+ import org .hdf5javalib .hdffile .dataobjects .messages .HdfMessage ;
9+ import org .hdf5javalib .hdffile .dataobjects .messages .LinkMessage ;
10+ import org .hdf5javalib .hdfjava .HdfDataFile ;
11+ import org .hdf5javalib .hdfjava .HdfDataset ;
12+ import org .hdf5javalib .hdfjava .HdfFileReader ;
13+ import org .slf4j .Logger ;
14+ import org .slf4j .LoggerFactory ;
15+
16+ import java .io .IOException ;
17+ import java .lang .reflect .InvocationTargetException ;
18+ import java .nio .channels .SeekableByteChannel ;
19+ import java .nio .file .Files ;
20+ import java .nio .file .Path ;
21+ import java .nio .file .StandardOpenOption ;
22+ import java .util .Arrays ;
23+ import java .util .Comparator ;
24+ import java .util .Optional ;
25+ import java .util .stream .Collectors ;
26+
27+ /**
28+ * Utility class for displaying HDF5 dataset data and managing attributes.
29+ * <p>
30+ * The {@code HdfDisplayUtils} class provides methods to display scalar and vector data
31+ * from HDF5 datasets using a {@link TypedDataSource}, as well as to create version attributes
32+ * for datasets. It supports various data types and formats the output for easy inspection,
33+ * handling both primitive and array types.
34+ * </p>
35+ */
36+ public class HdfDisplayCountUtils {
37+ private static final Logger log = LoggerFactory .getLogger (HdfDisplayCountUtils .class );
38+ public static final String UNDEFINED = "<Undefined>" ;
39+ private static final String STREAM_EQUALS = " stream = " ;
40+
41+ public static void displayLinkMessages (HdfObjectHeaderPrefix objectHeader ) {
42+ for ( HdfMessage hdfMessage : objectHeader .getHeaderMessages ()) {
43+ if ( hdfMessage instanceof LinkMessage ) {
44+ LinkMessage linkMessage = (LinkMessage ) hdfMessage ;
45+ System .out .println ("\t LinkMessage: " + linkMessage .toString ());
46+ }
47+
48+ }
49+ }
50+
51+ // Define a functional interface for actions that may need channel, dataset, and reader
52+ @ FunctionalInterface
53+ interface FileAction {
54+ void perform (SeekableByteChannel channel , HdfDataset dataSet , HdfFileReader reader ) throws Exception ;
55+ }
56+
57+ public static String undefinedArrayToString (HdfFixedPoint [] values ) {
58+ if (values == null || values .length == 0 ) {
59+ return "Not Present" ;
60+ }
61+ StringBuilder sb = new StringBuilder ("[" );
62+ for (int i = 0 ; i < values .length ; i ++) {
63+ sb .append (values [i ].isUndefined ()?UNDEFINED :values [i ].toString () );
64+ if (i != values .length - 1 ) {
65+ sb .append (", " );
66+ }
67+ }
68+ sb .append (']' );
69+ return sb .toString ();
70+ }
71+
72+ // Generalized method to process the file and apply a custom action per dataset
73+ private static void processFile (Path filePath , FileAction action ) {
74+ try (SeekableByteChannel channel = Files .newByteChannel (filePath , StandardOpenOption .READ )) {
75+ HdfFileReader reader = new HdfFileReader (channel ).readFile ();
76+ for (HdfDataset dataSet : reader .getDatasets ()) {
77+ System .out .println ("{} " + dataSet );
78+ // log.info("{} ", dataSet);
79+ action .perform (channel , dataSet , reader );
80+ }
81+ } catch (Exception e ) {
82+ log .error ("Exception in processFile: {}" , filePath , e );
83+ }
84+ }
85+
86+ public static void displayFileAttr (Path filePath ) {
87+ processFile (filePath , (channel , dataSet , reader ) -> displayAttributes (dataSet ));
88+ }
89+
90+ public static void displayFile (Path filePath ) {
91+ processFile (filePath , HdfDisplayCountUtils ::displayData );
92+ }
93+
94+ public static void displayAttributes (HdfDataset dataSet ) throws InvocationTargetException , InstantiationException , IllegalAccessException , IOException {
95+ for (AttributeMessage message : dataSet .getAttributeMessages ()) {
96+ HdfDataHolder dataHolder = message .getHdfDataHolder ();
97+ if (dataHolder .getDimensionality () == 1 ) {
98+ HdfData [] data = dataHolder .getAll (HdfData [].class );
99+ log .info ("Data = {}" , Arrays .toString (data ));
100+ } else if (dataHolder .getDimensionality () == 2 ) {
101+ HdfData [][] data = dataHolder .getAll (HdfData [][].class );
102+ for (HdfData [] row : data ) {
103+ log .info ("Row = {}" , Arrays .toString (row ));
104+ }
105+ }
106+ }
107+ }
108+
109+ // public static String getDataObjectFullName(HdfDataObject hdfDataObject) {
110+ // List<String> parents = new ArrayList<>();
111+ // HdfDataObject currentNode = hdfDataObject;
112+ // while(currentNode.getParent() != null) {
113+ // parents.add(currentNode.getObjectName());
114+ // currentNode = currentNode.getParent().getDataObject();
115+ // }
116+ // Collections.reverse(parents);
117+ // String objectPathString = '/' + currentNode.getObjectName() + String.join("/", parents);
118+ // return objectPathString;
119+ // }
120+
121+ public static void displayData (SeekableByteChannel channel , HdfDataset ds , HdfFileReader reader ) throws Exception {
122+ log .debug ("Dataset path: {}" , ds .getObjectPath ());
123+ if (ds .hasData ()) {
124+ switch (ds .getDimensionality ()) {
125+ case 0 :
126+ displayScalarData (channel , ds , HdfData .class , reader );
127+ break ;
128+ case 1 :
129+ displayVectorData (channel , ds , HdfData .class , reader );
130+ break ;
131+ case 2 :
132+ displayMatrixData (channel , ds , HdfData .class , reader );
133+ break ;
134+ default :
135+ displayNDimData (channel , ds , HdfData .class , reader );
136+ break ;
137+
138+ }
139+ } else if (ds .isDataset () && ds .getHardLink () != null ) {
140+ log .info ("{}: HARDLINK = {} " , ds .getObjectName (), ds .getHardLink ());
141+ }
142+ }
143+
144+ /**
145+ * Displays scalar data from a dataset.
146+ * <p>
147+ * Reads and prints the scalar value from the dataset using both direct reading and
148+ * streaming methods, formatting the output with the dataset name and type information.
149+ * </p>
150+ *
151+ * @param fileChannel the seekable byte channel for reading the HDF5 file
152+ * @param dataSet the dataset to read from
153+ * @param clazz the class type of the data
154+ * @param hdfDataFile the HDF5 file context
155+ * @param <T> the type of the data
156+ * @throws IOException if an I/O error occurs
157+ */
158+ public static <T > void displayScalarData (SeekableByteChannel fileChannel , HdfDataset dataSet , Class <T > clazz , HdfDataFile hdfDataFile ) throws IOException , InvocationTargetException , InstantiationException , IllegalAccessException {
159+ TypedDataSource <T > dataSource = new TypedDataSource <>(fileChannel , hdfDataFile , dataSet , clazz );
160+
161+ // Optional<String> max = dataSource.parallelStreamScalar().map(h->h.toString()).max(Comparator.naturalOrder());
162+ // System.out.println(dataSet.getObjectPath() + " stream count = " + max.orElse("NO MAX"));
163+ long count = dataSource .parallelStreamScalar ().count ();
164+ System .out .println (dataSet .getObjectPath () + " stream count = " + String .format ("%,d" , count ) + ":" + dataSet .getDatatype ().toString ());
165+ }
166+
167+ /**
168+ * Displays vector data from a dataset.
169+ * <p>
170+ * Reads and prints the vector data from the dataset using both direct reading and
171+ * streaming methods, formatting the output with type information and a comma-separated
172+ * list of values.
173+ * </p>
174+ *
175+ * @param fileChannel the seekable byte channel for reading the HDF5 file
176+ * @param dataSet the dataset to read from
177+ * @param clazz the class type of the data elements
178+ * @param hdfDataFile the HDF5 file context
179+ * @param <T> the type of the data elements
180+ * @throws IOException if an I/O error occurs
181+ */
182+ public static <T > void displayVectorData (SeekableByteChannel fileChannel , HdfDataset dataSet , Class <T > clazz , HdfDataFile hdfDataFile ) throws IOException , InvocationTargetException , InstantiationException , IllegalAccessException {
183+ TypedDataSource <T > dataSource = new TypedDataSource <>(fileChannel , hdfDataFile , dataSet , clazz );
184+
185+ // T[] resultArray = dataSource.readVector();
186+ // log.info("{} read = {}", displayType(clazz, resultArray), displayValue(resultArray));
187+
188+ // Optional<String> max = dataSource.parallelStreamVector().map(h->h.toString()).max(Comparator.naturalOrder());
189+ // System.out.println(dataSet.getObjectPath() + " stream count = " + max.orElse("NO MAX"));
190+
191+ long count = dataSource .parallelStreamVector ().count ();
192+ System .out .println (dataSet .getObjectPath () + " stream count = " + String .format ("%,d" , count ) + ":" + dataSet .getDatatype ().toString ());
193+ }
194+
195+ /**
196+ * Displays vector data from a dataset.
197+ * <p>
198+ * Reads and prints the vector data from the dataset using both direct reading and
199+ * streaming methods, formatting the output with type information and a comma-separated
200+ * list of values.
201+ * </p>
202+ *
203+ * @param fileChannel the seekable byte channel for reading the HDF5 file
204+ * @param dataSet the dataset to read from
205+ * @param clazz the class type of the data elements
206+ * @param hdfDataFile the HDF5 file context
207+ * @param <T> the type of the data elements
208+ * @throws IOException if an I/O error occurs
209+ */
210+ public static <T > void displayMatrixData (SeekableByteChannel fileChannel , HdfDataset dataSet , Class <T > clazz , HdfDataFile hdfDataFile ) throws IOException , InvocationTargetException , InstantiationException , IllegalAccessException {
211+ TypedDataSource <T > dataSource = new TypedDataSource <>(fileChannel , hdfDataFile , dataSet , clazz );
212+
213+ long count = dataSource .parallelStreamMatrix ().count ();
214+ System .out .println (dataSet .getObjectPath () + " stream count = " + String .format ("%,d" , count ) + ":" + dataSet .getDatatype ().toString ());
215+ }
216+
217+ /**
218+ * Displays vector data from a dataset.
219+ * <p>
220+ * Reads and prints the vector data from the dataset using both direct reading and
221+ * streaming methods, formatting the output with type information and a comma-separated
222+ * list of values.
223+ * </p>
224+ *
225+ * @param fileChannel the seekable byte channel for reading the HDF5 file
226+ * @param dataSet the dataset to read from
227+ * @param clazz the class type of the data elements
228+ * @param hdfDataFile the HDF5 file context
229+ * @param <T> the type of the data elements
230+ * @throws IOException if an I/O error occurs
231+ */
232+ private static <T > void displayNDimData (SeekableByteChannel fileChannel , HdfDataset dataSet , Class <T > clazz , HdfDataFile hdfDataFile ) throws IOException , InvocationTargetException , InstantiationException , IllegalAccessException {
233+ TypedDataSource <T > dataSource = new TypedDataSource <>(fileChannel , hdfDataFile , dataSet , clazz );
234+
235+ // String readResult = flattenedArrayToString(dataSource.readFlattened(), dataSource.getShape());
236+ // log.info("read = {}", readResult);
237+
238+ long count = dataSource .parallelStreamFlattened ().count ();
239+ System .out .println (dataSet .getObjectPath () + " stream count = " + String .format ("%,d" , count ) + ":" + dataSet .getDatatype ().toString ());
240+ }
241+
242+ }
0 commit comments