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.lang.reflect.Array;
017import java.math.BigInteger;
018import java.text.NumberFormat;
019import java.util.HashMap;
020import java.util.List;
021
022import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
023import org.eclipse.nebula.widgets.nattable.data.convert.DisplayConverter;
024import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
025
026import hdf.hdf5lib.exceptions.HDF5Exception;
027import hdf.object.CompoundDataFormat;
028import hdf.object.DataFormat;
029import hdf.object.Datatype;
030import hdf.object.h5.H5Datatype;
031import hdf.view.Tools;
032
033/**
034 * A Factory class to return a concrete class implementing the IDisplayConverter
035 * interface in order to convert data values into human-readable forms in a NatTable.
036 * The returned class is also responsible for converting the human-readable form back
037 * into real data when writing the data object back to the file.
038 *
039 * @author Jordan T. Henderson
040 * @version 1.0 2/9/2019
041 *
042 */
043public class DataDisplayConverterFactory
044{
045    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DataDisplayConverterFactory.class);
046
047    /**
048     * To keep things clean from an API perspective, keep a static reference to the last
049     * CompoundDataFormat that was passed in. This keeps us from needing to pass the
050     * CompoundDataFormat object as a parameter to every DataDisplayConverter class,
051     * since it's really only needed by the CompoundDataDisplayConverter.
052     */
053    private static DataFormat dataFormatReference = null;
054
055    /**
056     * Get the Data Display Converter for the supplied data object
057     *
058     * @param dataObject
059     *        the data object
060     *
061     * @return the converter instance
062     *
063     * @throws Exception if a failure occurred
064     */
065    public static HDFDisplayConverter getDataDisplayConverter(final DataFormat dataObject) throws Exception {
066        if (dataObject == null) {
067            log.debug("getDataDisplayConverter(DataFormat): data object is null");
068            return null;
069        }
070
071        dataFormatReference = dataObject;
072
073        HDFDisplayConverter converter = getDataDisplayConverter(dataObject.getDatatype());
074
075        return converter;
076    }
077
078    private static final HDFDisplayConverter getDataDisplayConverter(final Datatype dtype) throws Exception {
079        HDFDisplayConverter converter = null;
080
081        try {
082            if (dtype.isCompound())
083                converter = new CompoundDataDisplayConverter(dtype);
084            else if (dtype.isArray())
085                converter = new ArrayDataDisplayConverter(dtype);
086            else if (dtype.isVLEN() && !dtype.isVarStr())
087                converter = new VlenDataDisplayConverter(dtype);
088            else if (dtype.isString() || dtype.isVarStr())
089                converter = new StringDataDisplayConverter(dtype);
090            else if (dtype.isChar())
091                converter = new CharDataDisplayConverter(dtype);
092            else if (dtype.isInteger() || dtype.isFloat())
093                converter = new NumericalDataDisplayConverter(dtype);
094            else if (dtype.isEnum())
095                converter = new EnumDataDisplayConverter(dtype);
096            else if (dtype.isOpaque() || dtype.isBitField())
097                converter = new BitfieldDataDisplayConverter(dtype);
098            else if (dtype.isRef())
099                converter = new RefDataDisplayConverter(dtype);
100        }
101        catch (Exception ex) {
102            log.debug("getDataDisplayConverter(Datatype): error occurred in retrieving a DataDisplayConverter: ", ex);
103            converter = null;
104        }
105
106        /*
107         * Try to use a default converter.
108         */
109        if (converter == null) {
110            log.debug("getDataDisplayConverter(Datatype): using a default data display converter");
111
112            converter = new HDFDisplayConverter(dtype);
113        }
114
115        return converter;
116    }
117
118    /** the HDF extension for data converters */
119    public static class HDFDisplayConverter extends DisplayConverter
120    {
121        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(HDFDisplayConverter.class);
122
123        /** the number format type */
124        protected NumberFormat     numberFormat = null;
125        /** if the data shows in hex format */
126        protected boolean          showAsHex = false;
127        /** if data shows in binary format */
128        protected boolean          showAsBin = false;
129        /** if the enum mapped value is shown */
130        protected boolean          isEnumConverted = false;
131
132        /**
133         * This field is only used for CompoundDataDisplayConverters, but when the
134         * top-level DisplayConverter is a "container" type, such as an
135         * ArrayDataDisplayConverter, we have to set this field and pass it through in
136         * case there is a CompoundDataDisplayConverter at the bottom of the chain.
137         */
138        /** the "container" type row index */
139        protected int              cellRowIdx;
140        /** the "container" type column index */
141        protected int              cellColIdx;
142
143        /** create a HDF data converter
144         *
145         * @param dtype
146         *        the datatype for conversion
147         */
148        HDFDisplayConverter(final Datatype dtype) {
149            cellRowIdx = -1;
150            cellColIdx = -1;
151        }
152
153        @Override
154        public Object canonicalToDisplayValue(Object value) {
155            log.trace("canonicalToDisplayValue({}): start", value);
156
157            if (value instanceof String)
158                return value;
159
160            if (value == null) {
161                log.debug("canonicalToDisplayValue({}): value is null", value);
162                return DataFactoryUtils.nullStr;
163            }
164
165            return value;
166        }
167
168        @Override
169        public Object displayToCanonicalValue(Object value) {
170            log.trace("displayToCanonicalValue({}): start", value);
171            return value;
172        }
173
174        /**
175         * set the number format type
176         *
177         * @param format
178         *        the data format
179         */
180        public void setNumberFormat(NumberFormat format) {
181            numberFormat = format;
182        }
183
184        /**
185         * set if the data shows in hex format
186         *
187         * @param asHex
188         *        if the data shows as hex format
189         */
190        public void setShowAsHex(boolean asHex) {
191            showAsHex = asHex;
192        }
193
194        /**
195         * set if data shows in binary format
196         *
197         * @param asBin
198         *        if the data shows as binary format
199         */
200        public void setShowAsBin(boolean asBin) {
201            showAsBin = asBin;
202        }
203
204        /**
205         * set if the enum mapped value is shown
206         *
207         * @param convert
208         *        if the enum data should be converted
209         */
210        public void setConvertEnum(boolean convert) {
211            isEnumConverted = convert;
212        }
213    }
214
215    private static class CompoundDataDisplayConverter extends HDFDisplayConverter
216    {
217        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CompoundDataDisplayConverter.class);
218
219        private final HashMap<Integer, Integer> baseConverterIndexMap;
220        private final HashMap<Integer, Integer> relCmpdStartIndexMap;
221        private final HDFDisplayConverter[]     memberTypeConverters;
222        private final StringBuilder             buffer;
223        private final int                       nTotFields;
224
225        CompoundDataDisplayConverter(final Datatype dtype) throws Exception {
226            super(dtype);
227
228            if (!dtype.isCompound()) {
229                log.debug("datatype is not a compound type");
230                throw new Exception("CompoundDataDisplayConverter: datatype is not a compound type");
231            }
232
233            CompoundDataFormat compoundFormat = (CompoundDataFormat) dataFormatReference;
234
235            List<Datatype> localSelectedTypes = DataFactoryUtils.filterNonSelectedMembers(compoundFormat, dtype);
236
237            log.trace("setting up {} base HDFDisplayConverters", localSelectedTypes.size());
238
239            memberTypeConverters = new HDFDisplayConverter[localSelectedTypes.size()];
240            for (int i = 0; i < memberTypeConverters.length; i++) {
241                log.trace("retrieving DataDisplayConverter for member {}", i);
242
243                try {
244                    memberTypeConverters[i] = getDataDisplayConverter(localSelectedTypes.get(i));
245
246                    /*
247                     * Make base datatype converters inherit the data conversion settings.
248                     */
249                    memberTypeConverters[i].setShowAsHex(this.showAsHex);
250                    memberTypeConverters[i].setShowAsBin(this.showAsBin);
251                    memberTypeConverters[i].setNumberFormat(this.numberFormat);
252                    memberTypeConverters[i].setConvertEnum(this.isEnumConverted);
253                }
254                catch (Exception ex) {
255                    log.debug("failed to retrieve DataDisplayConverter for member {}: ", i, ex);
256                    memberTypeConverters[i] = null;
257                }
258            }
259
260            /*
261             * Build necessary index maps.
262             */
263            HashMap<Integer, Integer>[] maps = DataFactoryUtils.buildIndexMaps(compoundFormat, localSelectedTypes);
264            baseConverterIndexMap = maps[DataFactoryUtils.COL_TO_BASE_CLASS_MAP_INDEX];
265            relCmpdStartIndexMap = maps[DataFactoryUtils.CMPD_START_IDX_MAP_INDEX];
266
267            log.trace("index maps built: baseConverterIndexMap = {}, relColIdxMap = {}",
268                    baseConverterIndexMap, relCmpdStartIndexMap);
269
270            if (baseConverterIndexMap.size() == 0) {
271                log.debug("base DataDisplayConverter index mapping is invalid - size 0");
272                throw new Exception("CompoundDataDisplayConverter: invalid DataDisplayConverter mapping of size 0 built");
273            }
274
275            if (relCmpdStartIndexMap.size() == 0) {
276                log.debug("compound field start index mapping is invalid - size 0");
277                throw new Exception("CompoundDataDisplayConverter: invalid compound field start index mapping of size 0 built");
278            }
279
280            nTotFields = baseConverterIndexMap.size();
281
282            buffer = new StringBuilder();
283        }
284
285        @Override
286        public Object canonicalToDisplayValue(ILayerCell cell, IConfigRegistry configRegistry, Object value) {
287            cellRowIdx = cell.getRowIndex();
288            cellColIdx = cell.getColumnIndex() % nTotFields;
289            return canonicalToDisplayValue(value);
290        }
291
292        @Override
293        public Object canonicalToDisplayValue(Object value) {
294            log.trace("canonicalToDisplayValue({}): start", value);
295
296            if (value instanceof String)
297                return value;
298
299            if (value == null) {
300                log.debug("canonicalToDisplayValue({}): value is null", value);
301                return DataFactoryUtils.nullStr;
302            }
303
304            buffer.setLength(0); // clear the old string
305
306            try {
307                if (cellColIdx >= nTotFields)
308                    cellColIdx %= nTotFields;
309
310                if (value instanceof List) {
311                    /*
312                     * For Arrays of Compounds, we convert an entire list of data.
313                     */
314                    List<?> cmpdList = (List<?>) value;
315
316                    buffer.append("{");
317                    for (int i = 0; i < memberTypeConverters.length; i++) {
318                        if (i > 0)
319                            buffer.append(", ");
320
321                        Object curObject = cmpdList.get(i);
322                        if (curObject instanceof List)
323                            buffer.append(memberTypeConverters[i].canonicalToDisplayValue(curObject));
324                        else {
325                            Object dataArrayValue = Array.get(curObject, cellRowIdx);
326                            buffer.append(memberTypeConverters[i].canonicalToDisplayValue(dataArrayValue));
327                        }
328                    }
329                    buffer.append("}");
330                }
331                else {
332                    HDFDisplayConverter converter = memberTypeConverters[baseConverterIndexMap.get(cellColIdx)];
333                    converter.cellRowIdx = cellRowIdx;
334                    converter.cellColIdx = cellColIdx - relCmpdStartIndexMap.get(cellColIdx);
335
336                    buffer.append(converter.canonicalToDisplayValue(value));
337                }
338            }
339            catch (Exception ex) {
340                log.debug("canonicalToDisplayValue({}): failure: ", value, ex);
341                buffer.setLength(0);
342                buffer.append(DataFactoryUtils.errStr);
343            }
344
345            return buffer;
346        }
347
348        @Override
349        public void setNumberFormat(NumberFormat format) {
350            super.setNumberFormat(format);
351
352            for (int i = 0; i < memberTypeConverters.length; i++)
353                memberTypeConverters[i].setNumberFormat(format);
354        }
355
356        @Override
357        public void setShowAsHex(boolean asHex) {
358            super.setShowAsHex(asHex);
359
360            for (int i = 0; i < memberTypeConverters.length; i++)
361                memberTypeConverters[i].setShowAsHex(asHex);
362        }
363
364        @Override
365        public void setShowAsBin(boolean asBin) {
366            super.setShowAsBin(asBin);
367
368            for (int i = 0; i < memberTypeConverters.length; i++)
369                memberTypeConverters[i].setShowAsBin(asBin);
370        }
371
372        @Override
373        public void setConvertEnum(boolean convert) {
374            super.setConvertEnum(convert);
375
376            for (int i = 0; i < memberTypeConverters.length; i++)
377                memberTypeConverters[i].setConvertEnum(convert);
378        }
379
380    }
381
382    private static class ArrayDataDisplayConverter extends HDFDisplayConverter
383    {
384        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ArrayDataDisplayConverter.class);
385
386        private final HDFDisplayConverter baseTypeConverter;
387        private final StringBuilder       buffer;
388
389        ArrayDataDisplayConverter(final Datatype dtype) throws Exception {
390            super(dtype);
391
392            if (!dtype.isArray()) {
393                log.debug("exit: datatype is not an array type");
394                throw new Exception("ArrayDataDisplayConverter: datatype is not an array type");
395            }
396
397            Datatype baseType = dtype.getDatatypeBase();
398
399            if (baseType == null) {
400                log.debug("exit: base datatype is null");
401                throw new Exception("ArrayDataDisplayConverter: base datatype is null");
402            }
403
404            try {
405                baseTypeConverter = getDataDisplayConverter(baseType);
406
407                /*
408                 * Make base datatype converter inherit the data conversion settings.
409                 */
410                baseTypeConverter.setShowAsHex(this.showAsHex);
411                baseTypeConverter.setShowAsBin(this.showAsBin);
412                baseTypeConverter.setNumberFormat(this.numberFormat);
413                baseTypeConverter.setConvertEnum(this.isEnumConverted);
414            }
415            catch (Exception ex) {
416                log.debug("exit: couldn't get DataDisplayConverter for base datatype: ", ex);
417                throw new Exception("ArrayDataDisplayConverter: couldn't get DataDisplayConverter for base datatype: " + ex.getMessage());
418            }
419
420            buffer = new StringBuilder();
421        }
422
423        @Override
424        public Object canonicalToDisplayValue(ILayerCell cell, IConfigRegistry configRegistry, Object value) {
425            cellRowIdx = cell.getRowIndex();
426            cellColIdx = cell.getColumnIndex();
427            return canonicalToDisplayValue(value);
428        }
429
430        @Override
431        public Object canonicalToDisplayValue(Object value) {
432            log.trace("canonicalToDisplayValue({}): start", value);
433
434            if (value instanceof String)
435                return value;
436
437            if (value == null) {
438                log.debug("canonicalToDisplayValue({}): value is null", value);
439                return DataFactoryUtils.nullStr;
440            }
441
442            buffer.setLength(0); // clear the old string
443
444            /*
445             * Pass the cell's row and column index down in case there is a
446             * CompoundDataDisplayConverter at the bottom of the chain.
447             */
448            baseTypeConverter.cellRowIdx = cellRowIdx;
449            baseTypeConverter.cellColIdx = cellColIdx;
450
451            try {
452                Object obj;
453                Object convertedValue;
454                int arrLen = Array.getLength(value);
455
456                log.trace("canonicalToDisplayValue({}): array length={}", value, arrLen);
457
458                if (!(baseTypeConverter instanceof CompoundDataDisplayConverter))
459                    buffer.append("[");
460
461                for (int i = 0; i < arrLen; i++) {
462                    if (i > 0)
463                        buffer.append(", ");
464
465                    obj = Array.get(value, i);
466
467                    convertedValue = baseTypeConverter.canonicalToDisplayValue(obj);
468
469                    buffer.append(convertedValue);
470                }
471
472                if (!(baseTypeConverter instanceof CompoundDataDisplayConverter))
473                    buffer.append("]");
474            }
475            catch (Exception ex) {
476                log.debug("canonicalToDisplayValue({}): failure: ", value, ex);
477                buffer.setLength(0);
478                buffer.append(DataFactoryUtils.errStr);
479            }
480
481            return buffer;
482        }
483
484        @Override
485        public void setNumberFormat(NumberFormat format) {
486            super.setNumberFormat(format);
487
488            baseTypeConverter.setNumberFormat(format);
489        }
490
491        @Override
492        public void setShowAsHex(boolean asHex) {
493            super.setShowAsHex(asHex);
494
495            baseTypeConverter.setShowAsHex(asHex);
496        }
497
498        @Override
499        public void setShowAsBin(boolean asBin) {
500            super.setShowAsBin(asBin);
501
502            baseTypeConverter.setShowAsBin(asBin);
503        }
504
505        @Override
506        public void setConvertEnum(boolean convert) {
507            super.setConvertEnum(convert);
508
509            baseTypeConverter.setConvertEnum(convert);
510        }
511    }
512
513    private static class VlenDataDisplayConverter extends HDFDisplayConverter
514    {
515        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(VlenDataDisplayConverter.class);
516
517        private final HDFDisplayConverter baseTypeConverter;
518
519        VlenDataDisplayConverter(final Datatype dtype) throws Exception {
520            super(dtype);
521
522            if (!dtype.isVLEN() || dtype.isVarStr()) {
523                log.debug("exit: datatype is not a variable-length type or is a variable-length string type (use StringDataDisplayConverter)");
524                throw new Exception("VlenDataDisplayConverter: datatype is not a variable-length type or is a variable-length string type (use StringDataDisplayConverter)");
525            }
526
527            Datatype baseType = dtype.getDatatypeBase();
528
529            if (baseType == null) {
530                log.debug("base datatype is null");
531                throw new Exception("VlenDataDisplayConverter: base datatype is null");
532            }
533
534            try {
535                baseTypeConverter = getDataDisplayConverter(baseType);
536
537                /*
538                 * Make base datatype converter inherit the data conversion settings.
539                 */
540                baseTypeConverter.setShowAsHex(this.showAsHex);
541                baseTypeConverter.setShowAsBin(this.showAsBin);
542                baseTypeConverter.setNumberFormat(this.numberFormat);
543                baseTypeConverter.setConvertEnum(this.isEnumConverted);
544            }
545            catch (Exception ex) {
546                log.debug("couldn't get DataDisplayConverter for base datatype: ", ex);
547                throw new Exception("VlenDataDisplayConverter: couldn't get DataDisplayConverter for base datatype: " + ex.getMessage());
548            }
549        }
550
551        @Override
552        public void setNumberFormat(NumberFormat format) {
553            super.setNumberFormat(format);
554
555            baseTypeConverter.setNumberFormat(format);
556        }
557
558        @Override
559        public void setShowAsHex(boolean asHex) {
560            super.setShowAsHex(asHex);
561
562            baseTypeConverter.setShowAsHex(asHex);
563        }
564
565        @Override
566        public void setShowAsBin(boolean asBin) {
567            super.setShowAsBin(asBin);
568
569            baseTypeConverter.setShowAsBin(asBin);
570        }
571
572        @Override
573        public void setConvertEnum(boolean convert) {
574            super.setConvertEnum(convert);
575
576            baseTypeConverter.setConvertEnum(convert);
577        }
578    }
579
580    private static class StringDataDisplayConverter extends HDFDisplayConverter
581    {
582        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(StringDataDisplayConverter.class);
583
584        StringDataDisplayConverter(final Datatype dtype) throws Exception {
585            super(dtype);
586
587            if (!dtype.isString() && !dtype.isVarStr()) {
588                log.debug("datatype is not a string type");
589                throw new Exception("StringDataDisplayConverter: datatype is not a string type");
590            }
591        }
592    }
593
594    private static class CharDataDisplayConverter extends HDFDisplayConverter
595    {
596        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CharDataDisplayConverter.class);
597
598        CharDataDisplayConverter(final Datatype dtype) throws Exception {
599            super(dtype);
600
601            if (!dtype.isChar()) {
602                log.debug("datatype is not a character type");
603                throw new Exception("CharDataDisplayConverter: datatype is not a character type");
604            }
605        }
606
607        @Override
608        public Object displayToCanonicalValue(Object value) {
609            char charValue = ((String) value).charAt(0);
610            return (int) charValue;
611        }
612    }
613
614    private static class NumericalDataDisplayConverter extends HDFDisplayConverter
615    {
616        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NumericalDataDisplayConverter.class);
617
618        private final StringBuilder buffer;
619        private final long          typeSize;
620        private final boolean       isUINT64;
621
622        NumericalDataDisplayConverter(final Datatype dtype) throws Exception {
623            super(dtype);
624
625            if (!dtype.isInteger() && !dtype.isFloat()) {
626                log.debug("datatype is not an integer or floating-point type");
627                throw new Exception("NumericalDataDisplayConverter: datatype is not an integer or floating-point type");
628            }
629
630            buffer = new StringBuilder();
631
632            typeSize = dtype.getDatatypeSize();
633            isUINT64 = dtype.isUnsigned() && (typeSize == 8);
634        }
635
636        @Override
637        public Object canonicalToDisplayValue(Object value) {
638            log.trace("canonicalToDisplayValue({}): start", value);
639
640            if (value instanceof String)
641                return value;
642
643            if (value == null) {
644                log.debug("canonicalToDisplayValue({}): value is null", value);
645                return DataFactoryUtils.nullStr;
646            }
647
648            buffer.setLength(0); // clear the old string
649
650            try {
651                if (showAsHex) {
652                    if (isUINT64)
653                        buffer.append(Tools.toHexString((BigInteger) value, 8));
654                    else
655                        buffer.append(Tools.toHexString(Long.valueOf(value.toString()), (int) typeSize));
656                }
657                else if (showAsBin) {
658                    if (isUINT64)
659                        buffer.append(Tools.toBinaryString((BigInteger) value, 8));
660                    else
661                        buffer.append(Tools.toBinaryString(Long.valueOf(value.toString()), (int) typeSize));
662                }
663                else if (numberFormat != null) {
664                    buffer.append(numberFormat.format(value));
665                }
666                else {
667                    buffer.append(value.toString());
668                }
669            }
670            catch (Exception ex) {
671                log.debug("canonicalToDisplayValue({}): failure: ", value, ex);
672                buffer.setLength(0);
673                buffer.append(DataFactoryUtils.errStr);
674            }
675
676            return buffer;
677        }
678    }
679
680    private static class EnumDataDisplayConverter extends HDFDisplayConverter
681    {
682        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EnumDataDisplayConverter.class);
683
684        private final StringBuilder buffer;
685        private final H5Datatype    enumType;
686
687        EnumDataDisplayConverter(final Datatype dtype) throws Exception {
688            super(dtype);
689
690            if (!dtype.isEnum()) {
691                log.debug("datatype is not an enum type");
692                throw new Exception("EnumDataDisplayConverter: datatype is not an enum type");
693            }
694
695            buffer = new StringBuilder();
696
697            enumType = (H5Datatype) dtype;
698        }
699
700        @Override
701        public Object canonicalToDisplayValue(Object value) {
702            log.trace("canonicalToDisplayValue({}): start", value);
703
704            if (value instanceof String)
705                return value;
706
707            if (value == null) {
708                log.debug("canonicalToDisplayValue({}): value is null", value);
709                return DataFactoryUtils.nullStr;
710            }
711
712            buffer.setLength(0); // clear the old string
713
714            try {
715                if (isEnumConverted) {
716                    String[] retValues = null;
717
718                    try {
719                        retValues = enumType.convertEnumValueToName(value);
720                    }
721                    catch (HDF5Exception ex) {
722                        log.trace("canonicalToDisplayValue({}): Could not convert enum values to names: ", value, ex);
723                        retValues = null;
724                    }
725
726                    if (retValues != null)
727                        buffer.append(retValues[0]);
728                    else
729                        buffer.append(DataFactoryUtils.nullStr);
730                }
731                else
732                    buffer.append(value);
733            }
734            catch (Exception ex) {
735                log.debug("canonicalToDisplayValue({}): failure: ", value, ex);
736                buffer.setLength(0);
737                buffer.append(DataFactoryUtils.errStr);
738            }
739
740            return buffer;
741        }
742    }
743
744    private static class BitfieldDataDisplayConverter extends HDFDisplayConverter
745    {
746        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BitfieldDataDisplayConverter.class);
747
748        private final StringBuilder buffer;
749        private final boolean       isOpaque;
750
751        BitfieldDataDisplayConverter(final Datatype dtype) throws Exception {
752            super(dtype);
753
754            if (!dtype.isBitField() && !dtype.isOpaque()) {
755                log.debug("datatype is not a bitfield or opaque type");
756                throw new Exception("BitfieldDataDisplayConverter: datatype is not a bitfield or opaque type");
757            }
758
759            buffer = new StringBuilder();
760
761            isOpaque = dtype.isOpaque();
762        }
763
764        @Override
765        public Object canonicalToDisplayValue(Object value) {
766            log.trace("canonicalToDisplayValue({}): start", value);
767
768            if (value instanceof String)
769                return value;
770
771            if (value == null) {
772                log.debug("canonicalToDisplayValue({}): value is null", value);
773                return DataFactoryUtils.nullStr;
774            }
775
776            buffer.setLength(0); // clear the old string
777
778            try {
779                for (int i = 0; i < ((byte[]) value).length; i++) {
780                    if (i > 0)
781                        buffer.append(isOpaque ? " " : ":");
782
783                    buffer.append(Tools.toHexString((((byte[]) value)[i]), 1));
784                }
785            }
786            catch (Exception ex) {
787                log.debug("canonicalToDisplayValue({}): failure: ", value, ex);
788                buffer.setLength(0);
789                buffer.append(DataFactoryUtils.errStr);
790            }
791
792            return buffer;
793        }
794    }
795
796    private static class RefDataDisplayConverter extends HDFDisplayConverter
797    {
798        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RefDataDisplayConverter.class);
799
800        RefDataDisplayConverter(final Datatype dtype) throws Exception {
801            super(dtype);
802
803            if (!dtype.isRef()) {
804                log.debug("datatype is not a reference type");
805                throw new Exception("RefDataDisplayConverter: datatype is not a reference type");
806            }
807        }
808    }
809
810}