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}