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