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