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