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