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        private final boolean isFLT16;
716
717        NumericalDataDisplayConverter(final Datatype dtype) throws Exception
718        {
719            super(dtype);
720
721            if (!dtype.isInteger() && !dtype.isFloat()) {
722                log.debug("datatype is not an integer or floating-point type");
723                throw new Exception(
724                    "NumericalDataDisplayConverter: datatype is not an integer or floating-point type");
725            }
726
727            buffer = new StringBuilder();
728
729            typeSize = dtype.getDatatypeSize();
730            isUINT64 = dtype.isUnsigned() && (typeSize == 8);
731            isFLT16  = dtype.isFloat() && (typeSize == 2);
732        }
733
734        @Override
735        public Object canonicalToDisplayValue(Object value)
736        {
737            log.trace("canonicalToDisplayValue({}): start", value);
738
739            if (value instanceof String)
740                return value;
741
742            if (value == null) {
743                log.debug("canonicalToDisplayValue({}): value is null", value);
744                return DataFactoryUtils.nullStr;
745            }
746
747            buffer.setLength(0); // clear the old string
748
749            try {
750                if (showAsHex) {
751                    if (isUINT64)
752                        buffer.append(Tools.toHexString((BigInteger)value, 8));
753                    else
754                        buffer.append(Tools.toHexString(Long.valueOf(value.toString()), (int)typeSize));
755                }
756                else if (showAsBin) {
757                    if (isUINT64)
758                        buffer.append(Tools.toBinaryString((BigInteger)value, 8));
759                    else
760                        buffer.append(Tools.toBinaryString(Long.valueOf(value.toString()), (int)typeSize));
761                }
762                else if (numberFormat != null) {
763                    buffer.append(numberFormat.format(value));
764                }
765                else {
766                    buffer.append(value.toString());
767                }
768            }
769            catch (Exception ex) {
770                log.debug("canonicalToDisplayValue({}): failure: ", value, ex);
771                buffer.setLength(0);
772                buffer.append(DataFactoryUtils.errStr);
773            }
774
775            return buffer;
776        }
777    }
778
779    private static class EnumDataDisplayConverter extends HDFDisplayConverter {
780        private static final Logger log = LoggerFactory.getLogger(EnumDataDisplayConverter.class);
781
782        private final StringBuilder buffer;
783        private final H5Datatype enumType;
784
785        EnumDataDisplayConverter(final Datatype dtype) throws Exception
786        {
787            super(dtype);
788
789            if (!dtype.isEnum()) {
790                log.debug("datatype is not an enum type");
791                throw new Exception("EnumDataDisplayConverter: datatype is not an enum type");
792            }
793
794            buffer = new StringBuilder();
795
796            enumType = (H5Datatype)dtype;
797        }
798
799        @Override
800        public Object canonicalToDisplayValue(Object value)
801        {
802            log.trace("canonicalToDisplayValue({}): start", value);
803
804            if (value instanceof String)
805                return value;
806
807            if (value == null) {
808                log.debug("canonicalToDisplayValue({}): value is null", value);
809                return DataFactoryUtils.nullStr;
810            }
811
812            buffer.setLength(0); // clear the old string
813
814            try {
815                if (isEnumConverted) {
816                    String[] retValues = null;
817
818                    try {
819                        retValues = enumType.convertEnumValueToName(value);
820                    }
821                    catch (HDF5Exception ex) {
822                        log.trace("canonicalToDisplayValue({}): Could not convert enum values to names: ",
823                                  value, ex);
824                        retValues = null;
825                    }
826
827                    if (retValues != null)
828                        buffer.append(retValues[0]);
829                    else
830                        buffer.append(DataFactoryUtils.nullStr);
831                }
832                else
833                    buffer.append(value);
834            }
835            catch (Exception ex) {
836                log.debug("canonicalToDisplayValue({}): failure: ", value, ex);
837                buffer.setLength(0);
838                buffer.append(DataFactoryUtils.errStr);
839            }
840
841            return buffer;
842        }
843    }
844
845    private static class BitfieldDataDisplayConverter extends HDFDisplayConverter {
846        private static final Logger log = LoggerFactory.getLogger(BitfieldDataDisplayConverter.class);
847
848        private final StringBuilder buffer;
849        private final boolean isOpaque;
850
851        BitfieldDataDisplayConverter(final Datatype dtype) throws Exception
852        {
853            super(dtype);
854
855            if (!dtype.isBitField() && !dtype.isOpaque()) {
856                log.debug("datatype is not a bitfield or opaque type");
857                throw new Exception(
858                    "BitfieldDataDisplayConverter: datatype is not a bitfield or opaque type");
859            }
860
861            buffer = new StringBuilder();
862
863            isOpaque = dtype.isOpaque();
864        }
865
866        @Override
867        public Object canonicalToDisplayValue(Object value)
868        {
869            log.trace("canonicalToDisplayValue({}): start", value);
870
871            if (value instanceof String)
872                return value;
873
874            if (value == null) {
875                log.debug("canonicalToDisplayValue({}): value is null", value);
876                return DataFactoryUtils.nullStr;
877            }
878
879            buffer.setLength(0); // clear the old string
880
881            try {
882                for (int i = 0; i < ((byte[])value).length; i++) {
883                    if (i > 0)
884                        buffer.append(isOpaque ? " " : ":");
885
886                    buffer.append(Tools.toHexString((((byte[])value)[i]), 1));
887                }
888            }
889            catch (Exception ex) {
890                log.debug("canonicalToDisplayValue({}): failure: ", value, ex);
891                buffer.setLength(0);
892                buffer.append(DataFactoryUtils.errStr);
893            }
894
895            return buffer;
896        }
897    }
898
899    private static class RefDataDisplayConverter extends HDFDisplayConverter {
900        private static final Logger log = LoggerFactory.getLogger(RefDataDisplayConverter.class);
901
902        private final StringBuilder buffer;
903
904        RefDataDisplayConverter(final Datatype dtype) throws Exception
905        {
906            super(dtype);
907
908            if (!dtype.isRef()) {
909                log.debug("datatype is not a reference type");
910                throw new Exception("RefDataDisplayConverter: datatype is not a reference type");
911            }
912
913            buffer = new StringBuilder();
914        }
915
916        @Override
917        public Object canonicalToDisplayValue(ILayerCell cell, IConfigRegistry configRegistry, Object value)
918        {
919            cellRowIdx = cell.getRowIndex();
920            cellColIdx = cell.getColumnIndex();
921            log.trace("canonicalToDisplayValue({}) cellRowIdx={} cellColIdx={}: start", value, cellRowIdx,
922                      cellColIdx);
923            return canonicalToDisplayValue(value);
924        }
925
926        @Override
927        public Object canonicalToDisplayValue(Object value)
928        {
929            log.trace("canonicalToDisplayValue({}): start", value);
930
931            if (value instanceof String)
932                return value;
933
934            if (value == null) {
935                log.debug("canonicalToDisplayValue({}): value is null", value);
936                return DataFactoryUtils.nullStr;
937            }
938
939            buffer.setLength(0); // clear the old string
940
941            try {
942                Object obj;
943                Object convertedValue;
944                int arrLen = Array.getLength(value);
945
946                log.trace("canonicalToDisplayValue({}): array length={}", value, arrLen);
947
948                for (int i = 0; i < arrLen; i++) {
949                    if (i > 0)
950                        buffer.append(", ");
951
952                    obj = Array.get(value, i);
953
954                    buffer.append(obj);
955                }
956            }
957            catch (Exception ex) {
958                log.debug("canonicalToDisplayValue({}): failure: ", value, ex);
959                buffer.setLength(0);
960                buffer.append(DataFactoryUtils.errStr);
961            }
962
963            return buffer;
964        }
965    }
966}