• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gephi / graphstore / #550

23 Apr 2026 07:06PM UTC coverage: 91.037% (-0.005%) from 91.042%
#550

push

mbastian
Fix minor issues on parsing and formatting

8 of 9 new or added lines in 3 files covered. (88.89%)

43 existing lines in 1 file now uncovered.

11742 of 12898 relevant lines covered (91.04%)

0.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.92
/src/main/java/org/gephi/graph/api/AttributeUtils.java
1
/*
2
 * Copyright 2012-2013 Gephi Consortium
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
 * use this file except in compliance with the License. You may obtain a copy of
6
 * the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
 * License for the specific language governing permissions and limitations under
14
 * the License.
15
 */
16

17
package org.gephi.graph.api;
18

19
import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
20
import it.unimi.dsi.fastutil.booleans.BooleanOpenHashSet;
21
import it.unimi.dsi.fastutil.bytes.Byte2ObjectOpenHashMap;
22
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
23
import it.unimi.dsi.fastutil.bytes.ByteOpenHashSet;
24
import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap;
25
import it.unimi.dsi.fastutil.chars.CharArrayList;
26
import it.unimi.dsi.fastutil.chars.CharOpenHashSet;
27
import it.unimi.dsi.fastutil.doubles.Double2ObjectOpenHashMap;
28
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
29
import it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet;
30
import it.unimi.dsi.fastutil.floats.Float2ObjectOpenHashMap;
31
import it.unimi.dsi.fastutil.floats.FloatArrayList;
32
import it.unimi.dsi.fastutil.floats.FloatOpenHashSet;
33
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
34
import it.unimi.dsi.fastutil.ints.IntArrayList;
35
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
36
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
37
import it.unimi.dsi.fastutil.longs.LongArrayList;
38
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
39
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
40
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
41
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
42
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
43
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
44
import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet;
45
import java.lang.reflect.Array;
46
import java.math.BigDecimal;
47
import java.math.BigInteger;
48
import java.text.DecimalFormat;
49
import java.text.DecimalFormatSymbols;
50
import java.time.Instant;
51
import java.time.OffsetDateTime;
52
import java.time.ZoneId;
53
import java.time.ZonedDateTime;
54
import java.time.format.DateTimeFormatter;
55
import java.time.format.DateTimeFormatterBuilder;
56
import java.time.format.DateTimeParseException;
57
import java.time.temporal.ChronoField;
58
import java.util.Collections;
59
import java.util.HashMap;
60
import java.util.HashSet;
61
import java.util.concurrent.ConcurrentHashMap;
62
import java.util.List;
63
import java.util.Locale;
64
import java.util.Map;
65
import java.util.Set;
66
import org.gephi.graph.api.types.IntervalBooleanMap;
67
import org.gephi.graph.api.types.IntervalByteMap;
68
import org.gephi.graph.api.types.IntervalCharMap;
69
import org.gephi.graph.api.types.IntervalDoubleMap;
70
import org.gephi.graph.api.types.IntervalFloatMap;
71
import org.gephi.graph.api.types.IntervalIntegerMap;
72
import org.gephi.graph.api.types.IntervalLongMap;
73
import org.gephi.graph.api.types.IntervalMap;
74
import org.gephi.graph.api.types.IntervalSet;
75
import org.gephi.graph.api.types.IntervalShortMap;
76
import org.gephi.graph.api.types.IntervalStringMap;
77
import org.gephi.graph.api.types.TimeMap;
78
import org.gephi.graph.api.types.TimeSet;
79
import org.gephi.graph.api.types.TimestampBooleanMap;
80
import org.gephi.graph.api.types.TimestampByteMap;
81
import org.gephi.graph.api.types.TimestampCharMap;
82
import org.gephi.graph.api.types.TimestampDoubleMap;
83
import org.gephi.graph.api.types.TimestampFloatMap;
84
import org.gephi.graph.api.types.TimestampIntegerMap;
85
import org.gephi.graph.api.types.TimestampLongMap;
86
import org.gephi.graph.api.types.TimestampMap;
87
import org.gephi.graph.api.types.TimestampSet;
88
import org.gephi.graph.api.types.TimestampShortMap;
89
import org.gephi.graph.api.types.TimestampStringMap;
90
import org.gephi.graph.impl.ArraysParser;
91
import org.gephi.graph.impl.FormattingAndParsingUtils;
92
import org.gephi.graph.impl.GraphStoreConfiguration;
93
import org.gephi.graph.impl.IntervalsParser;
94
import org.gephi.graph.impl.TimestampsParser;
95

96
/**
97
 * Set of utility methods to manipulate supported attribute types.
98
 * <p>
99
 * The attribute system is built with a set of supported column types. This
100
 * class contains utilities to parse and convert supported types. It also
101
 * contains utilities to manipulate primitive arrays (the preferred array type)
102
 * and date/time types. Default time zone for parsing/printing dates is UTC.
103
 */
104
public class AttributeUtils {
105

106
    private static final Set<Class> SUPPORTED_TYPES;
107
    private static final Map<Class, Class> TYPES_STANDARDIZATION;
108
    private static final DateTimeFormatter DATE_TIME_PARSER;
109
    private static final DateTimeFormatter DATE_PRINTER;
110
    private static final DateTimeFormatter DATE_TIME_PRINTER;
111
    private static final DecimalFormat TIMESTAMP_PRINTER;
112

113
    // These are used to avoid creating a lot of new instances of
114
    // DateTimeFormatter
115
    private static final Map<ZoneId, DateTimeFormatter> DATE_PRINTERS_BY_TIMEZONE;
116
    private static final Map<ZoneId, DateTimeFormatter> DATE_TIME_PRINTERS_BY_TIMEZONE;
117
    private static final Map<ZoneId, DateTimeFormatter> DATE_TIME_PARSERS_BY_TIMEZONE;
118

119
    // Collection types to speedup lookup
120
    private static final Set<Class> TYPED_LIST_TYPES;
121
    private static final Set<Class> TYPED_SET_TYPES;
122
    private static final Set<Class> TYPED_MAP_TYPES;
123

124
    static {
125
        final Set<Class> supportedTypes = new HashSet<>();
1✔
126

127
        // Primitives
128
        supportedTypes.add(Boolean.class);
1✔
129
        supportedTypes.add(boolean.class);
1✔
130
        supportedTypes.add(Integer.class);
1✔
131
        supportedTypes.add(int.class);
1✔
132
        supportedTypes.add(Short.class);
1✔
133
        supportedTypes.add(short.class);
1✔
134
        supportedTypes.add(Long.class);
1✔
135
        supportedTypes.add(long.class);
1✔
136
        supportedTypes.add(BigInteger.class);
1✔
137
        supportedTypes.add(Byte.class);
1✔
138
        supportedTypes.add(byte.class);
1✔
139
        supportedTypes.add(Float.class);
1✔
140
        supportedTypes.add(float.class);
1✔
141
        supportedTypes.add(Double.class);
1✔
142
        supportedTypes.add(double.class);
1✔
143
        supportedTypes.add(BigDecimal.class);
1✔
144
        supportedTypes.add(Character.class);
1✔
145
        supportedTypes.add(char.class);
1✔
146

147
        // Objects
148
        supportedTypes.add(String.class);
1✔
149

150
        // Instant
151
        supportedTypes.add(Instant.class);
1✔
152

153
        // Primitives Array
154
        supportedTypes.add(Boolean[].class);
1✔
155
        supportedTypes.add(boolean[].class);
1✔
156
        supportedTypes.add(Integer[].class);
1✔
157
        supportedTypes.add(int[].class);
1✔
158
        supportedTypes.add(Short[].class);
1✔
159
        supportedTypes.add(short[].class);
1✔
160
        supportedTypes.add(Long[].class);
1✔
161
        supportedTypes.add(long[].class);
1✔
162
        supportedTypes.add(BigInteger[].class);
1✔
163
        supportedTypes.add(Byte[].class);
1✔
164
        supportedTypes.add(byte[].class);
1✔
165
        supportedTypes.add(Float[].class);
1✔
166
        supportedTypes.add(float[].class);
1✔
167
        supportedTypes.add(Double[].class);
1✔
168
        supportedTypes.add(double[].class);
1✔
169
        supportedTypes.add(BigDecimal[].class);
1✔
170
        supportedTypes.add(Character[].class);
1✔
171
        supportedTypes.add(char[].class);
1✔
172

173
        // Objects array
174
        supportedTypes.add(String[].class);
1✔
175

176
        // Dynamic (timestamps)
177
        supportedTypes.add(TimestampSet.class);
1✔
178
        supportedTypes.add(TimestampBooleanMap.class);
1✔
179
        supportedTypes.add(TimestampIntegerMap.class);
1✔
180
        supportedTypes.add(TimestampShortMap.class);
1✔
181
        supportedTypes.add(TimestampLongMap.class);
1✔
182
        supportedTypes.add(TimestampByteMap.class);
1✔
183
        supportedTypes.add(TimestampFloatMap.class);
1✔
184
        supportedTypes.add(TimestampDoubleMap.class);
1✔
185
        supportedTypes.add(TimestampCharMap.class);
1✔
186
        supportedTypes.add(TimestampStringMap.class);
1✔
187

188
        // Dynamic (intervals)
189
        supportedTypes.add(IntervalSet.class);
1✔
190
        supportedTypes.add(IntervalBooleanMap.class);
1✔
191
        supportedTypes.add(IntervalIntegerMap.class);
1✔
192
        supportedTypes.add(IntervalShortMap.class);
1✔
193
        supportedTypes.add(IntervalLongMap.class);
1✔
194
        supportedTypes.add(IntervalByteMap.class);
1✔
195
        supportedTypes.add(IntervalFloatMap.class);
1✔
196
        supportedTypes.add(IntervalDoubleMap.class);
1✔
197
        supportedTypes.add(IntervalCharMap.class);
1✔
198
        supportedTypes.add(IntervalStringMap.class);
1✔
199

200
        // Lists, Maps, Sets
201
        supportedTypes.add(List.class);
1✔
202
        supportedTypes.add(Set.class);
1✔
203
        supportedTypes.add(Map.class);
1✔
204

205
        // Assign
206
        SUPPORTED_TYPES = Collections.unmodifiableSet(supportedTypes);
1✔
207

208
        // Primitive types standardization
209
        final Map<Class, Class> typesStandardization = new HashMap<>();
1✔
210
        typesStandardization.put(boolean.class, Boolean.class);
1✔
211
        typesStandardization.put(int.class, Integer.class);
1✔
212
        typesStandardization.put(short.class, Short.class);
1✔
213
        typesStandardization.put(long.class, Long.class);
1✔
214
        typesStandardization.put(byte.class, Byte.class);
1✔
215
        typesStandardization.put(float.class, Float.class);
1✔
216
        typesStandardization.put(double.class, Double.class);
1✔
217
        typesStandardization.put(char.class, Character.class);
1✔
218

219
        // Array standardization
220
        typesStandardization.put(Boolean[].class, boolean[].class);
1✔
221
        typesStandardization.put(Integer[].class, int[].class);
1✔
222
        typesStandardization.put(Short[].class, short[].class);
1✔
223
        typesStandardization.put(Long[].class, long[].class);
1✔
224
        typesStandardization.put(Byte[].class, byte[].class);
1✔
225
        typesStandardization.put(Float[].class, float[].class);
1✔
226
        typesStandardization.put(Double[].class, double[].class);
1✔
227
        typesStandardization.put(Character[].class, char[].class);
1✔
228

229
        // Assign
230
        TYPES_STANDARDIZATION = Collections.unmodifiableMap(typesStandardization);
1✔
231

232
        // Datetime - make sure UTC timezone is used by default
233
        DATE_TIME_PARSER = new DateTimeFormatterBuilder().parseCaseInsensitive()
1✔
234
                .appendOptional(DateTimeFormatter.ISO_DATE).appendOptional(DateTimeFormatter.ofPattern("yyyyMMdd"))
1✔
235
                .optionalStart().appendLiteral('T').append(DateTimeFormatter.ISO_TIME)
1✔
236
                .appendPattern("[.SSSSSSSSS][.SSSSSS][.SSS]").optionalEnd().optionalStart()
1✔
237
                .appendFraction(ChronoField.NANO_OF_SECOND, 9, 9, true).optionalEnd()
1✔
238
                // optional nanos with 6 digits (including decimal point)
239
                .optionalStart().appendFraction(ChronoField.NANO_OF_SECOND, 6, 6, true).optionalEnd()
1✔
240
                // optional nanos with 3 digits (including decimal point)
241
                .optionalStart().appendFraction(ChronoField.NANO_OF_SECOND, 3, 3, true).optionalEnd()
1✔
242
                .parseDefaulting(ChronoField.HOUR_OF_DAY, 0).parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
1✔
243
                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0).parseDefaulting(ChronoField.NANO_OF_SECOND, 0)
1✔
244
                .toFormatter().withZone(GraphStoreConfiguration.DEFAULT_TIME_ZONE);
1✔
245
        DATE_PRINTER = new DateTimeFormatterBuilder().parseCaseInsensitive().appendPattern("yyyy-MM-dd").toFormatter()
1✔
246
                .withZone(GraphStoreConfiguration.DEFAULT_TIME_ZONE);
1✔
247
        DATE_TIME_PRINTER = new DateTimeFormatterBuilder().parseCaseInsensitive()
1✔
248
                .append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('T').appendPattern("HH:mm:ss")
1✔
249
                .appendPattern(".SSS").parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
1✔
250
                .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0).parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
1✔
251
                .parseDefaulting(ChronoField.NANO_OF_SECOND, 0).appendOffset("+HH:MM", "Z").toFormatter()
1✔
252
                .withZone(GraphStoreConfiguration.DEFAULT_TIME_ZONE);
1✔
253

254
        DATE_PRINTERS_BY_TIMEZONE = new ConcurrentHashMap<>();
1✔
255
        DATE_TIME_PRINTERS_BY_TIMEZONE = new ConcurrentHashMap<>();
1✔
256
        DATE_TIME_PARSERS_BY_TIMEZONE = new ConcurrentHashMap<>();
1✔
257

258
        DATE_PRINTERS_BY_TIMEZONE.put(DATE_PRINTER.getZone(), DATE_PRINTER);
1✔
259
        DATE_TIME_PRINTERS_BY_TIMEZONE.put(DATE_TIME_PRINTER.getZone(), DATE_TIME_PRINTER);
1✔
260
        DATE_TIME_PARSERS_BY_TIMEZONE.put(DATE_TIME_PARSER.getZone(), DATE_TIME_PARSER);
1✔
261

262
        DecimalFormatSymbols decimalFormatSymbols = DecimalFormatSymbols.getInstance(Locale.ENGLISH);
1✔
263
        decimalFormatSymbols.setInfinity(FormattingAndParsingUtils.INFINITY);
1✔
264
        // 1 to 4 decimals
265
        TIMESTAMP_PRINTER = new DecimalFormat("0.0###", decimalFormatSymbols);
1✔
266

267
        // List types
268
        TYPED_LIST_TYPES = new HashSet<>();
1✔
269
        TYPED_LIST_TYPES.add(IntArrayList.class);
1✔
270
        TYPED_LIST_TYPES.add(FloatArrayList.class);
1✔
271
        TYPED_LIST_TYPES.add(DoubleArrayList.class);
1✔
272
        TYPED_LIST_TYPES.add(ShortArrayList.class);
1✔
273
        TYPED_LIST_TYPES.add(LongArrayList.class);
1✔
274
        TYPED_LIST_TYPES.add(ByteArrayList.class);
1✔
275
        TYPED_LIST_TYPES.add(BooleanArrayList.class);
1✔
276
        TYPED_LIST_TYPES.add(CharArrayList.class);
1✔
277

278
        // Set types
279
        TYPED_SET_TYPES = new HashSet<>();
1✔
280
        TYPED_SET_TYPES.add(IntOpenHashSet.class);
1✔
281
        TYPED_SET_TYPES.add(FloatOpenHashSet.class);
1✔
282
        TYPED_SET_TYPES.add(DoubleOpenHashSet.class);
1✔
283
        TYPED_SET_TYPES.add(ShortOpenHashSet.class);
1✔
284
        TYPED_SET_TYPES.add(LongOpenHashSet.class);
1✔
285
        TYPED_SET_TYPES.add(ByteOpenHashSet.class);
1✔
286
        TYPED_SET_TYPES.add(BooleanOpenHashSet.class);
1✔
287
        TYPED_SET_TYPES.add(CharOpenHashSet.class);
1✔
288

289
        // Map types
290
        TYPED_MAP_TYPES = new HashSet<>();
1✔
291
        TYPED_MAP_TYPES.add(Int2ObjectOpenHashMap.class);
1✔
292
        TYPED_MAP_TYPES.add(Float2ObjectOpenHashMap.class);
1✔
293
        TYPED_MAP_TYPES.add(Double2ObjectOpenHashMap.class);
1✔
294
        TYPED_MAP_TYPES.add(Short2ObjectOpenHashMap.class);
1✔
295
        TYPED_MAP_TYPES.add(Long2ObjectOpenHashMap.class);
1✔
296
        TYPED_MAP_TYPES.add(Byte2ObjectOpenHashMap.class);
1✔
297
        TYPED_MAP_TYPES.add(Char2ObjectOpenHashMap.class);
1✔
298
    }
1✔
299

300
    private AttributeUtils() {
301
        // Only static methods
302
    }
303

304
    private static DateTimeFormatter getDateTimeFormatterByTimeZone(Map<ZoneId, DateTimeFormatter> cache, DateTimeFormatter baseFormatter, ZoneId zoneId) {
305
        if (zoneId == null) {
1✔
306
            return baseFormatter;
1✔
307
        }
308

309
        return cache.computeIfAbsent(zoneId, z -> baseFormatter.withZone(z));
1✔
310
    }
311

312
    private static DateTimeFormatter getDateTimeParserByTimeZone(ZoneId zoneId) {
313
        return getDateTimeFormatterByTimeZone(DATE_TIME_PARSERS_BY_TIMEZONE, DATE_TIME_PARSER, zoneId);
1✔
314
    }
315

316
    private static DateTimeFormatter getDateTimePrinterByTimeZone(ZoneId zoneId) {
317
        return getDateTimeFormatterByTimeZone(DATE_TIME_PRINTERS_BY_TIMEZONE, DATE_TIME_PRINTER, zoneId);
1✔
318
    }
319

320
    private static DateTimeFormatter getDatePrinterByTimeZone(ZoneId zoneId) {
321
        return getDateTimeFormatterByTimeZone(DATE_PRINTERS_BY_TIMEZONE, DATE_PRINTER, zoneId);
1✔
322
    }
323

324
    /**
325
     * Returns the string representation of the given value.
326
     *
327
     * @param value value
328
     * @return string representation
329
     */
330
    public static String print(Object value) {
331
        return print(value, TimeFormat.DOUBLE, null);
1✔
332
    }
333

334
    /**
335
     * Returns the string representation of the given value.
336
     *
337
     * @param value value
338
     * @param timeFormat time format
339
     * @param zoneId time zone
340
     * @return string representation
341
     */
342
    public static String print(Object value, TimeFormat timeFormat, ZoneId zoneId) {
343
        if (value == null) {
1✔
344
            return "null";
1✔
345
        }
346
        if (value instanceof TimeSet) {
1✔
347
            return ((TimeSet) value).toString(timeFormat, zoneId);
1✔
348
        }
349
        if (value instanceof TimeMap) {
1✔
350
            return ((TimeMap) value).toString(timeFormat, zoneId);
1✔
351
        }
352
        if (value instanceof Instant) {
1✔
353
            return value.toString();
1✔
354
        }
355
        if (value.getClass().isArray()) {
1✔
356
            return printArray(value);
1✔
357
        }
358
        return value.toString();
1✔
359
    }
360

361
    /**
362
     * Parses the given string using the type class provided and returns an
363
     * instance.
364
     *
365
     * @param str string to parse
366
     * @param typeClass class of the desired type
367
     * @param zoneId time zone to use or null to use default time zone (UTC), for
368
     *        dynamic types and <code>Instant</code> only
369
     * @return an instance of the type class, or null if <em>str</em> is null or
370
     *         empty
371
     */
372
    public static Object parse(String str, Class typeClass, ZoneId zoneId) {
373
        if (str == null || str.isEmpty()) {
1✔
374
            return null;
1✔
375
        }
376

377
        if (str.equalsIgnoreCase("null")) {
1✔
378
            return null;
1✔
379
        }
380

381
        if (typeClass.isPrimitive()) {
1✔
382
            typeClass = getStandardizedType(typeClass);// For primitives we can
1✔
383
                                                       // use auto-unboxing
384
        }
385

386
        // Simple and primitive types:
387
        if (typeClass.equals(String.class)) {
1✔
388
            return str;
1✔
389
        } else if (typeClass.equals(Byte.class)) {
1✔
390
            return Byte.valueOf(str);
1✔
391
        } else if (typeClass.equals(Short.class)) {
1✔
392
            return Short.valueOf(str);
1✔
393
        } else if (typeClass.equals(Integer.class)) {
1✔
394
            return Integer.valueOf(str);
1✔
395
        } else if (typeClass.equals(Long.class)) {
1✔
396
            return Long.valueOf(str);
1✔
397
        } else if (typeClass.equals(Float.class)) {
1✔
398
            return Float.valueOf(str);
1✔
399
        } else if (typeClass.equals(Double.class)) {
1✔
400
            return Double.valueOf(str);
1✔
401
        } else if (typeClass.equals(BigInteger.class)) {
1✔
402
            return new BigInteger(str);
1✔
403
        } else if (typeClass.equals(BigDecimal.class)) {
1✔
404
            return new BigDecimal(str);
1✔
405
        } else if (typeClass.equals(Boolean.class)) {
1✔
406
            if (str.length() == 1) {
1✔
407
                if (str.charAt(0) == '1') {
1✔
408
                    return Boolean.TRUE;
1✔
409
                } else if (str.charAt(0) == '0') {
1✔
410
                    return Boolean.FALSE;
1✔
411
                }
412
            }
413
            return Boolean.valueOf(str);
1✔
414
        } else if (typeClass.equals(Character.class)) {
1✔
415
            if (str.length() > 1) {
1✔
416
                throw new IllegalArgumentException("The string has a length > 1");
1✔
417
            }
418
            return str.charAt(0);
1✔
419
        }
420

421
        // Instant
422
        if (typeClass.equals(Instant.class)) {
1✔
423
            double milliseconds = FormattingAndParsingUtils.parseDateTimeOrTimestamp(str, zoneId);
1✔
424
            return Instant.ofEpochMilli(Math.round(milliseconds));
1✔
425
        }
426

427
        // Interval types:
428
        if (typeClass.equals(IntervalSet.class)) {
1✔
429
            return IntervalsParser.parseIntervalSet(str, zoneId);
1✔
430
        } else if (typeClass.equals(IntervalStringMap.class)) {
1✔
431
            return IntervalsParser.parseIntervalMap(String.class, str, zoneId);
1✔
432
        } else if (typeClass.equals(IntervalByteMap.class)) {
1✔
433
            return IntervalsParser.parseIntervalMap(Byte.class, str, zoneId);
1✔
434
        } else if (typeClass.equals(IntervalShortMap.class)) {
1✔
435
            return IntervalsParser.parseIntervalMap(Short.class, str, zoneId);
1✔
436
        } else if (typeClass.equals(IntervalIntegerMap.class)) {
1✔
437
            return IntervalsParser.parseIntervalMap(Integer.class, str, zoneId);
1✔
438
        } else if (typeClass.equals(IntervalLongMap.class)) {
1✔
439
            return IntervalsParser.parseIntervalMap(Long.class, str, zoneId);
1✔
440
        } else if (typeClass.equals(IntervalFloatMap.class)) {
1✔
441
            return IntervalsParser.parseIntervalMap(Float.class, str, zoneId);
1✔
442
        } else if (typeClass.equals(IntervalDoubleMap.class)) {
1✔
443
            return IntervalsParser.parseIntervalMap(Double.class, str, zoneId);
1✔
444
        } else if (typeClass.equals(IntervalBooleanMap.class)) {
1✔
445
            return IntervalsParser.parseIntervalMap(Boolean.class, str, zoneId);
1✔
446
        } else if (typeClass.equals(IntervalCharMap.class)) {
1✔
447
            return IntervalsParser.parseIntervalMap(Character.class, str, zoneId);
1✔
448
        }
449

450
        // Timestamp types:
451
        if (typeClass.equals(TimestampSet.class)) {
1✔
452
            return TimestampsParser.parseTimestampSet(str, zoneId);
1✔
453
        } else if (typeClass.equals(TimestampStringMap.class)) {
1✔
454
            return TimestampsParser.parseTimestampMap(String.class, str, zoneId);
1✔
455
        } else if (typeClass.equals(TimestampByteMap.class)) {
1✔
456
            return TimestampsParser.parseTimestampMap(Byte.class, str, zoneId);
1✔
457
        } else if (typeClass.equals(TimestampShortMap.class)) {
1✔
458
            return TimestampsParser.parseTimestampMap(Short.class, str, zoneId);
1✔
459
        } else if (typeClass.equals(TimestampIntegerMap.class)) {
1✔
460
            return TimestampsParser.parseTimestampMap(Integer.class, str, zoneId);
1✔
461
        } else if (typeClass.equals(TimestampLongMap.class)) {
1✔
462
            return TimestampsParser.parseTimestampMap(Long.class, str, zoneId);
1✔
463
        } else if (typeClass.equals(TimestampFloatMap.class)) {
1✔
464
            return TimestampsParser.parseTimestampMap(Float.class, str, zoneId);
1✔
465
        } else if (typeClass.equals(TimestampDoubleMap.class)) {
1✔
466
            return TimestampsParser.parseTimestampMap(Double.class, str, zoneId);
1✔
467
        } else if (typeClass.equals(TimestampBooleanMap.class)) {
1✔
468
            return TimestampsParser.parseTimestampMap(Boolean.class, str, zoneId);
1✔
469
        } else if (typeClass.equals(TimestampCharMap.class)) {
1✔
470
            return TimestampsParser.parseTimestampMap(Character.class, str, zoneId);
1✔
471
        }
472

473
        // Array types:
474
        if (typeClass.equals(boolean[].class)) {
1✔
475
            return ArraysParser.parseArrayAsPrimitiveArray(Boolean[].class, str);
1✔
476
        } else if (typeClass.equals(char[].class)) {
1✔
477
            return ArraysParser.parseArrayAsPrimitiveArray(Character[].class, str);
×
478
        } else if (typeClass.equals(byte[].class)) {
1✔
479
            return ArraysParser.parseArrayAsPrimitiveArray(Byte[].class, str);
1✔
480
        } else if (typeClass.equals(short[].class)) {
1✔
481
            return ArraysParser.parseArrayAsPrimitiveArray(Short[].class, str);
1✔
482
        } else if (typeClass.equals(int[].class)) {
1✔
483
            return ArraysParser.parseArrayAsPrimitiveArray(Integer[].class, str);
1✔
484
        } else if (typeClass.equals(long[].class)) {
1✔
485
            return ArraysParser.parseArrayAsPrimitiveArray(Long[].class, str);
1✔
486
        } else if (typeClass.equals(float[].class)) {
1✔
487
            return ArraysParser.parseArrayAsPrimitiveArray(Float[].class, str);
1✔
488
        } else if (typeClass.equals(double[].class)) {
1✔
489
            return ArraysParser.parseArrayAsPrimitiveArray(Double[].class, str);
1✔
490
        } else if (typeClass.equals(Boolean[].class) || typeClass.equals(String[].class) || typeClass
1✔
491
                .equals(Character[].class) || typeClass.equals(Byte[].class) || typeClass
1✔
492
                        .equals(Short[].class) || typeClass.equals(Integer[].class) || typeClass
1✔
493
                                .equals(Long[].class) || typeClass.equals(Float[].class) || typeClass
1✔
494
                                        .equals(Double[].class) || typeClass
1✔
495
                                                .equals(BigInteger[].class) || typeClass.equals(BigDecimal[].class)) {
1✔
496
            return ArraysParser.parseArray(typeClass, str);
1✔
497
        }
498

499
        throw new IllegalArgumentException("Unsupported type " + typeClass.getCanonicalName());
1✔
500
    }
501

502
    /**
503
     * Parses the given string using the type class provided and returns an
504
     * instance.
505
     *
506
     * Default time zone is used (UTC) for dynamic types (timestamps/intervals).
507
     *
508
     * @param str string to parse
509
     * @param typeClass class of the desired type
510
     * @return an instance of the type class, or null if <em>str</em> is null or
511
     *         empty
512
     */
513
    public static Object parse(String str, Class typeClass) {
514
        return parse(str, typeClass, null);
1✔
515
    }
516

517
    /**
518
     * Returns the primitive type for the given wrapped primitive.
519
     * <p>
520
     * Example: Returns <em>int.class</em> given <em>Integer.class</em>
521
     *
522
     * @param type type to get the primitive type from
523
     * @return primitive type
524
     */
525
    public static Class getPrimitiveType(Class type) {
526
        if (!type.isPrimitive()) {
1✔
527
            if (type.equals(Boolean.class)) {
1✔
528
                return boolean.class;
1✔
529
            } else if (type.equals(Integer.class)) {
1✔
530
                return int.class;
1✔
531
            } else if (type.equals(Short.class)) {
1✔
532
                return short.class;
1✔
533
            } else if (type.equals(Long.class)) {
1✔
534
                return long.class;
1✔
535
            } else if (type.equals(Byte.class)) {
1✔
536
                return byte.class;
1✔
537
            } else if (type.equals(Float.class)) {
1✔
538
                return float.class;
1✔
539
            } else if (type.equals(Double.class)) {
1✔
540
                return double.class;
1✔
541
            } else if (type.equals(Character.class)) {
1✔
542
                return char.class;
1✔
543
            }
544
        }
545
        throw new IllegalArgumentException("The type should be a wrapped primitive");
1✔
546
    }
547

548
    /**
549
     * Returns the primitive array given a wrapped primitive array.
550
     * <p>
551
     * Example: Returns <em>int[]</em> array given an <em>Integer[]</em> array
552
     *
553
     * @param array wrapped primitive array instance
554
     * @return primitive array instance
555
     * @throws IllegalArgumentException Thrown if any of the array values is null
556
     */
557
    public static Object getPrimitiveArray(Object[] array) {
558
        if (!isSupported(array.getClass())) {
1✔
559
            throw new IllegalArgumentException("Unsupported type " + array.getClass().getCanonicalName());
1✔
560
        }
561
        Class arrayClass = array.getClass().getComponentType();
1✔
562
        if (!arrayClass
1✔
563
                .isPrimitive() && (arrayClass == Double.class || arrayClass == Float.class || arrayClass == Long.class || arrayClass == Integer.class || arrayClass == Short.class || arrayClass == Character.class || arrayClass == Byte.class || arrayClass == Boolean.class)) {
1✔
564
            Class primitiveClass = getPrimitiveType(arrayClass);
1✔
565

566
            int arrayLength = array.length;
1✔
567
            Object primitiveArray = Array.newInstance(primitiveClass, arrayLength);
1✔
568

569
            for (int i = 0; i < arrayLength; i++) {
1✔
570
                Object obj = array[i];
1✔
571
                Array.set(primitiveArray, i, obj);
1✔
572
            }
573
            return primitiveArray;
1✔
574
        }
575
        return array;
1✔
576
    }
577

578
    /**
579
     * Returns the set of types supported.
580
     *
581
     * @return set of supported types
582
     */
583
    public static Set<Class> getSupportedTypes() {
584
        return SUPPORTED_TYPES;
1✔
585
    }
586

587
    /**
588
     * Returns true if <em>type</em> is a supported type.
589
     *
590
     * @param type type to test support
591
     * @return true if supported, false otherwise
592
     */
593
    public static boolean isSupported(Class type) {
594
        if (type == null) {
1✔
595
            throw new NullPointerException();
1✔
596
        }
597
        return SUPPORTED_TYPES.contains(type) || isCollectionType(type) || isMapType(type);
1✔
598
    }
599

600
    /**
601
     * Returns the standardized type for the given type class.
602
     * <p>
603
     * For instance, <code>getStandardizedType(int.class)</code> would return
604
     * <code>Integer.class</code>.
605
     *
606
     * @param type type to standardize
607
     * @return standardized type
608
     */
609
    public static Class getStandardizedType(Class type) {
610
        if (!isSupported(type)) {
1✔
611
            throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
1✔
612
        }
613
        Class t = TYPES_STANDARDIZATION.get(type);
1✔
614
        if (t != null) {
1✔
615
            return t;
1✔
616
        }
617
        return type;
1✔
618
    }
619

620
    /**
621
     * Returns true if <em>type</em> is a standardized type.
622
     * <p>
623
     * Non standardized types are transformed into standardized types using
624
     * {@link #getStandardizedType(java.lang.Class) }.
625
     *
626
     * @param type the type to test
627
     * @return true if <em>type</em> is standardized, false otherwise
628
     */
629
    public static boolean isStandardizedType(Class type) {
630
        if (!isSupported(type)) {
1✔
631
            throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
1✔
632
        }
633
        return TYPES_STANDARDIZATION.get(type) == null;
1✔
634
    }
635

636
    /**
637
     * Returns the dynamic timestamp map value type for the given type.
638
     *
639
     * @param type static type
640
     * @return timestamp map type
641
     */
642
    public static Class<? extends TimestampMap> getTimestampMapType(Class type) {
643
        if (!isSupported(type)) {
1✔
644
            throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
1✔
645
        }
646
        type = getStandardizedType(type);
1✔
647
        if (type.equals(Boolean.class)) {
1✔
648
            return TimestampBooleanMap.class;
1✔
649
        } else if (type.equals(Integer.class)) {
1✔
650
            return TimestampIntegerMap.class;
1✔
651
        } else if (type.equals(Short.class)) {
1✔
652
            return TimestampShortMap.class;
1✔
653
        } else if (type.equals(Long.class)) {
1✔
654
            return TimestampLongMap.class;
1✔
655
        } else if (type.equals(Byte.class)) {
1✔
656
            return TimestampByteMap.class;
1✔
657
        } else if (type.equals(Float.class)) {
1✔
658
            return TimestampFloatMap.class;
1✔
659
        } else if (type.equals(Double.class)) {
1✔
660
            return TimestampDoubleMap.class;
1✔
661
        } else if (type.equals(Character.class)) {
1✔
662
            return TimestampCharMap.class;
1✔
663
        } else if (type.equals(String.class)) {
1✔
664
            return TimestampStringMap.class;
1✔
665
        }
666
        throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
×
667
    }
668

669
    /**
670
     * Returns the dynamic timestamp map value type for the given type.
671
     *
672
     * @param type static type
673
     * @return timestamp map type
674
     */
675
    public static Class<? extends IntervalMap> getIntervalMapType(Class type) {
676
        if (!isSupported(type)) {
1✔
677
            throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
1✔
678
        }
679
        type = getStandardizedType(type);
1✔
680
        if (type.equals(Boolean.class)) {
1✔
681
            return IntervalBooleanMap.class;
1✔
682
        } else if (type.equals(Integer.class)) {
1✔
683
            return IntervalIntegerMap.class;
1✔
684
        } else if (type.equals(Short.class)) {
1✔
685
            return IntervalShortMap.class;
1✔
686
        } else if (type.equals(Long.class)) {
1✔
687
            return IntervalLongMap.class;
1✔
688
        } else if (type.equals(Byte.class)) {
1✔
689
            return IntervalByteMap.class;
1✔
690
        } else if (type.equals(Float.class)) {
1✔
691
            return IntervalFloatMap.class;
1✔
692
        } else if (type.equals(Double.class)) {
1✔
693
            return IntervalDoubleMap.class;
1✔
694
        } else if (type.equals(Character.class)) {
1✔
695
            return IntervalCharMap.class;
1✔
696
        } else if (type.equals(String.class)) {
1✔
697
            return IntervalStringMap.class;
1✔
698
        }
699
        throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
×
700
    }
701

702
    /**
703
     * Returns the static type for the given time map type.
704
     *
705
     * @param type time map type
706
     * @return static type
707
     */
708
    public static Class getStaticType(Class<? extends TimeMap> type) {
709
        if (!isSupported(type)) {
1✔
710
            throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
1✔
711
        }
712

713
        if (type.equals(TimestampBooleanMap.class) || type.equals(IntervalBooleanMap.class)) {
1✔
714
            return Boolean.class;
1✔
715
        } else if (type.equals(TimestampIntegerMap.class) || type.equals(IntervalIntegerMap.class)) {
1✔
716
            return Integer.class;
1✔
717
        } else if (type.equals(TimestampShortMap.class) || type.equals(IntervalShortMap.class)) {
1✔
718
            return Short.class;
1✔
719
        } else if (type.equals(TimestampLongMap.class) || type.equals(IntervalLongMap.class)) {
1✔
720
            return Long.class;
1✔
721
        } else if (type.equals(TimestampByteMap.class) || type.equals(IntervalByteMap.class)) {
1✔
722
            return Byte.class;
1✔
723
        } else if (type.equals(TimestampFloatMap.class) || type.equals(IntervalFloatMap.class)) {
1✔
724
            return Float.class;
1✔
725
        } else if (type.equals(TimestampDoubleMap.class) || type.equals(IntervalDoubleMap.class)) {
1✔
726
            return Double.class;
1✔
727
        } else if (type.equals(TimestampCharMap.class) || type.equals(IntervalCharMap.class)) {
1✔
728
            return Character.class;
1✔
729
        } else if (type.equals(TimestampStringMap.class) || type.equals(IntervalStringMap.class)) {
1✔
730
            return String.class;
1✔
731
        }
732
        throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
×
733
    }
734

735
    /**
736
     * Transform the given <em>value</em> instance in a standardized type if
737
     * necessary.
738
     * <p>
739
     * This function transforms wrapped primitive arrays in primitive arrays.
740
     *
741
     * @param value value to standardize
742
     * @return standardized value, or <em>value</em> if already standardized
743
     */
744
    public static Object standardizeValue(Object value) {
745
        if (value == null) {
1✔
746
            return null;
1✔
747
        }
748
        Class type = value.getClass();
1✔
749
        if (!isSupported(type)) {
1✔
750
            throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
1✔
751
        }
752
        if (type.isArray() && !type.getComponentType().isPrimitive()) {
1✔
753
            return getPrimitiveArray((Object[]) value);
1✔
754
        }
755
        if (List.class.isAssignableFrom(type)) {
1✔
756
            return getStandardizedList((List) value);
1✔
757
        } else if (Set.class.isAssignableFrom(type)) {
1✔
758
            return getStandardizedSet((Set) value);
1✔
759
        } else if (Map.class.isAssignableFrom(type)) {
1✔
760
            return getStandardizedMap((Map) value);
1✔
761
        }
762
        return value;
1✔
763
    }
764

765
    private static List getStandardizedList(List list) {
766
        Class listClass = list.getClass();
1✔
767
        if (TYPED_LIST_TYPES.contains(listClass)) {
1✔
768
            return list;
×
769
        }
770

771
        Class oCls = null;
1✔
772
        for (Object o : list) {
1✔
773
            if (o != null) {
1✔
774
                if (oCls == null) {
1✔
775
                    oCls = o.getClass();
1✔
776
                } else if (!o.getClass().equals(oCls)) {
1✔
777
                    throw new IllegalArgumentException("The list contains mixed classes");
1✔
778
                }
779
            }
780
        }
1✔
781
        if (oCls != null && !(isSimpleType(oCls) || isArrayType(oCls))) {
1✔
782
            throw new IllegalArgumentException(
1✔
783
                    "The list contains unsupported type " + oCls.getCanonicalName());
784
        }
1✔
785
        if (oCls != null) {
1✔
786
            if (oCls.equals(Integer.class)) {
1✔
787
                return new IntArrayList(list);
1✔
UNCOV
788
            } else if (oCls.equals(Float.class)) {
×
789
                return new FloatArrayList(list);
1✔
UNCOV
790
            } else if (oCls.equals(Double.class)) {
×
791
                return new DoubleArrayList(list);
1✔
UNCOV
792
            } else if (oCls.equals(Short.class)) {
×
793
                return new ShortArrayList(list);
1✔
UNCOV
794
            } else if (oCls.equals(Byte.class)) {
×
795
                return new ByteArrayList(list);
1✔
UNCOV
796
            } else if (oCls.equals(Long.class)) {
×
797
                return new LongArrayList(list);
1✔
UNCOV
798
            } else if (oCls.equals(Boolean.class)) {
×
799
                return new BooleanArrayList(list);
1✔
UNCOV
800
            } else if (oCls.equals(Character.class)) {
×
801
                return new CharArrayList(list);
802
            }
803
        }
1✔
804
        List result = new ObjectArrayList(list.size());
1✔
805
        for (Object o : list) {
1✔
806
            result.add(standardizeValue(o));
1✔
807
        }
1✔
808
        return result;
809
    }
810

811
    private static Set getStandardizedSet(Set set) {
1✔
812
        Class setClass = set.getClass();
1✔
UNCOV
813
        if (TYPED_SET_TYPES.contains(setClass)) {
×
814
            return set;
815
        }
816

1✔
817
        Class oCls = null;
1✔
818
        for (Object o : set) {
1✔
819
            if (o != null) {
1✔
820
                if (oCls == null) {
1✔
821
                    oCls = o.getClass();
1✔
822
                } else if (!o.getClass().equals(oCls)) {
1✔
823
                    throw new IllegalArgumentException("The set contains mixed classes");
824
                }
825
            }
1✔
826
        }
1✔
827
        if (oCls != null && !(isSimpleType(oCls) || isArrayType(oCls))) {
1✔
828
            throw new IllegalArgumentException(
829
                    "The set contains unsupported type " + oCls.getCanonicalName());
1✔
830
        }
1✔
831
        if (oCls != null) {
1✔
832
            if (oCls.equals(Integer.class)) {
1✔
UNCOV
833
                return new IntOpenHashSet(set);
×
834
            } else if (oCls.equals(Float.class)) {
1✔
835
                return new FloatOpenHashSet(set);
×
836
            } else if (oCls.equals(Double.class)) {
1✔
837
                return new DoubleOpenHashSet(set);
×
838
            } else if (oCls.equals(Short.class)) {
1✔
839
                return new ShortOpenHashSet(set);
×
840
            } else if (oCls.equals(Byte.class)) {
1✔
841
                return new ByteOpenHashSet(set);
×
842
            } else if (oCls.equals(Long.class)) {
1✔
843
                return new LongOpenHashSet(set);
×
844
            } else if (oCls.equals(Boolean.class)) {
1✔
845
                return new BooleanOpenHashSet(set);
×
846
            } else if (oCls.equals(Character.class)) {
847
                return new CharOpenHashSet(set);
848
            }
1✔
849
        }
1✔
850
        Set result = new ObjectOpenHashSet(set.size());
1✔
851
        for (Object o : set) {
1✔
852
            result.add(standardizeValue(o));
1✔
853
        }
854
        return result;
855
    }
856

1✔
857
    private static Map getStandardizedMap(Map<?, ?> map) {
1✔
UNCOV
858
        Class mapClass = map.getClass();
×
859
        if (TYPED_MAP_TYPES.contains(mapClass)) {
860
            return map;
861
        }
1✔
862

1✔
863
        Class oCls = null;
1✔
864
        for (Map.Entry entry : map.entrySet()) {
1✔
865
            Object key = entry.getKey();
1✔
866
            Object value = entry.getValue();
1✔
867
            if (key != null) {
1✔
868
                if (oCls == null) {
1✔
869
                    oCls = key.getClass();
1✔
870
                } else if (!key.getClass().equals(oCls)) {
871
                    throw new IllegalArgumentException("The map contains mixed key classes");
872
                }
1✔
873
            }
1✔
874
            if (value != null && !(isSimpleType(value.getClass()) || isArrayType(value.getClass()))) {
1✔
875
                throw new IllegalArgumentException(
876
                        "The map contains unsupported value type " + value.getClass().getCanonicalName());
1✔
877
            }
1✔
878
        }
1✔
879
        if (oCls != null && !isSimpleType(oCls)) {
880
            throw new IllegalArgumentException(
1✔
881
                    "The map contains unsupported key type " + oCls.getCanonicalName());
1✔
882
        }
1✔
883
        if (oCls != null) {
1✔
UNCOV
884
            if (oCls.equals(Integer.class)) {
×
885
                return new Int2ObjectOpenHashMap(map);
1✔
UNCOV
886
            } else if (oCls.equals(Float.class)) {
×
887
                return new Float2ObjectOpenHashMap(map);
1✔
UNCOV
888
            } else if (oCls.equals(Double.class)) {
×
889
                return new Double2ObjectOpenHashMap(map);
1✔
UNCOV
890
            } else if (oCls.equals(Short.class)) {
×
891
                return new Short2ObjectOpenHashMap(map);
1✔
UNCOV
892
            } else if (oCls.equals(Byte.class)) {
×
893
                return new Byte2ObjectOpenHashMap(map);
1✔
UNCOV
894
            } else if (oCls.equals(Long.class)) {
×
895
                return new Long2ObjectOpenHashMap(map);
896
            } else if (oCls.equals(Character.class)) {
897
                return new Char2ObjectOpenHashMap(map);
1✔
898
            }
1✔
899
        }
1✔
900
        Map result = new Object2ObjectOpenHashMap(map.size());
1✔
901
        for (Map.Entry o : map.entrySet()) {
1✔
902
            result.put(o.getKey(), standardizeValue(o.getValue()));
903
        }
904
        return result;
905
    }
906

907
    /**
908
     * Returns true if <em>type</em> is a number type.
909
     * <p>
910
     * This can be true for static, arrays and dynamic types.
911
     *
912
     * @param type type to test
913
     * @return true if <em>type</em> is a number type, false otherwise
1✔
914
     */
1✔
915
    public static boolean isNumberType(Class type) {
916
        if (!isSupported(type)) {
1✔
917
            throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
1✔
918
        }
1✔
919
        type = getStandardizedType(type);
1✔
920
        return Number.class.isAssignableFrom(type) || int[].class.isAssignableFrom(type) || float[].class
1✔
921
                .isAssignableFrom(type) || double[].class.isAssignableFrom(type) || byte[].class
1✔
922
                        .isAssignableFrom(type) || short[].class.isAssignableFrom(type) || long[].class
1✔
923
                                .isAssignableFrom(type) || type.equals(TimestampIntegerMap.class) || type
1✔
924
                                        .equals(TimestampFloatMap.class) || type
1✔
925
                                                .equals(TimestampDoubleMap.class) || type
1✔
926
                                                        .equals(TimestampLongMap.class) || type
1✔
927
                                                                .equals(TimestampShortMap.class) || type
1✔
928
                                                                        .equals(TimestampByteMap.class) || type
1✔
929
                                                                                .equals(IntervalIntegerMap.class) || type
1✔
930
                                                                                        .equals(IntervalFloatMap.class) || type
1✔
931
                                                                                                .equals(IntervalDoubleMap.class) || type
1✔
932
                                                                                                        .equals(IntervalLongMap.class) || type
933
                                                                                                                .equals(IntervalShortMap.class) || type
934
                                                                                                                        .equals(IntervalByteMap.class);
935
    }
936

937
    /**
938
     * Returns true if <em>type</em> is a string type
939
     * <p>
940
     * This can be true for static, arrays and dynamic types.
941
     *
942
     * @param type type to test
943
     * @return true if <em>type</em> is a string type, false otherwise
1✔
944
     */
1✔
945
    public static boolean isStringType(Class type) {
946
        if (!isSupported(type)) {
1✔
947
            throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
1✔
948
        }
949
        return type.equals(String.class) || type.equals(String[].class) || type.equals(TimestampStringMap.class) || type
950
                .equals(IntervalStringMap.class);
951
    }
952

953
    /**
954
     * Returns true if <em>type</em> is a boolean type
955
     * <p>
956
     * This can be true for static, arrays and dynamic types.
957
     *
958
     * @param type type to test
959
     * @return true if <em>type</em> is a boolean type, false otherwise
1✔
960
     */
1✔
961
    public static boolean isBooleanType(Class type) {
962
        if (!isSupported(type)) {
1✔
963
            throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
1✔
964
        }
1✔
965
        type = getStandardizedType(type);
966
        return type.equals(Boolean.class) || type.equals(boolean[].class) || type
967
                .equals(TimestampBooleanMap.class) || type.equals(IntervalBooleanMap.class);
968
    }
969

970
    /**
971
     * Returns true if <em>type</em> is a dynamic type.
972
     *
973
     * @param type type to test
974
     * @return true if <em>type</em> is a dynamic type, false otherwise
1✔
975
     */
1✔
976
    public static boolean isDynamicType(Class type) {
1✔
977
        return (!type.equals(TimestampMap.class) && TimestampMap.class.isAssignableFrom(type)) || type
978
                .equals(TimestampSet.class) || (!type.equals(IntervalMap.class) && IntervalMap.class
979
                        .isAssignableFrom(type)) || type.equals(IntervalSet.class);
980
    }
981

982
    /**
983
     * Returns true if <em>type</em> is a simple type.
984
     * <p>
985
     * Simple types are primitives, String and wrapper types (e.g. Integer).
986
     *
987
     * @param type type to test
988
     * @return true if <em>type</em> is a simple type, false otherwise
1✔
989
     */
1✔
990
    public static boolean isSimpleType(Class type) {
991
        return (type
992
                .isPrimitive() && type != void.class) || type == Double.class || type == Float.class || type == Long.class || type == Integer.class || type == Short.class || type == Character.class || type == Byte.class || type == Boolean.class || type == String.class;
993
    }
994

995
    /**
996
     * Returns true if <em>type</em> is an array type.
997
     *
998
     * @param type type to test
999
     * @return true if <em>type</em> is an array type, false otherwise
1✔
1000
     */
1001
    public static boolean isArrayType(Class type) {
1002
        return type.isArray() && isSupported(type.getComponentType());
1003
    }
1004

1005
    /**
1006
     * Returns true if <em>type</em> is a collection type.
1007
     * <p>
1008
     * Collection types are either <em>List</em> or <em>Set</em>.
1009
     *
1010
     * @param type type to test
1011
     * @return true if <em>type</em> is a collection type, false otherwise
1✔
1012
     */
1013
    public static boolean isCollectionType(Class type) {
1014
        return List.class.isAssignableFrom(type) || Set.class.isAssignableFrom(type);
1015
    }
1016

1017
    /**
1018
     * Returns true if <em>type</em> is a map type.
1019
     * <p>
1020
     * Collection types implement the <em>Map</em> interface.
1021
     *
1022
     * @param type type to test
1023
     * @return true if <em>type</em> is a map type, false otherwise
1✔
1024
     */
1025
    public static boolean isMapType(Class type) {
1026
        return Map.class.isAssignableFrom(type);
1027
    }
1028

1029
    /**
1030
     * Returns the type name for the given type.
1031
     *
1032
     * @param type type to get its name
1033
     * @return type name
1✔
1034
     */
1✔
1035
    public static String getTypeName(Class type) {
1036
        if (!isSupported(type)) {
1✔
1037
            throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName());
1✔
1038
        }
1039
        type = getStandardizedType(type);
1040
        return type.getSimpleName().toLowerCase();
1041
    }
1042

1043
    /**
1044
     * Parses the given time and returns its milliseconds representation.
1045
     *
1046
     * @param dateTime type to parse
1047
     * @param zoneId time zone to use or null to use default time zone (UTC)
1048
     * @return milliseconds representation
1049
     * @throws DateTimeParseException if the time cannot be parsed
1✔
1050
     */
1✔
1051
    public static double parseDateTime(String dateTime, ZoneId zoneId) throws DateTimeParseException {
1✔
1052
        DateTimeFormatter dateTimeParserByTimeZone = getDateTimeParserByTimeZone(zoneId);
1053
        Instant instant = dateTimeParserByTimeZone.parse(dateTime, Instant::from);
1054
        return (double) instant.toEpochMilli();
1055
    }
1056

1057
    /**
1058
     * Parses the given time and returns its milliseconds representation. Default
1059
     * time zone is used (UTC).
1060
     *
1061
     * @param dateTime the type to parse
1062
     * @return milliseconds representation
1063
     * @throws DateTimeParseException if the time cannot be parsed
1✔
1064
     */
1065
    public static double parseDateTime(String dateTime) throws DateTimeParseException {
1066
        return parseDateTime(dateTime, null);
1067
    }
1068

1069
    /**
1070
     * Parses an ISO date with or without time or a timestamp (in milliseconds).
1071
     * Returns the date or timestamp converted to a timestamp in milliseconds.
1072
     *
1073
     * @param timeStr Date or timestamp string
1074
     * @param zoneId Time zone to use or null to use default time zone (UTC)
1075
     * @return Timestamp
1076
     * @throws DateTimeParseException if the time cannot be parsed
1✔
1077
     */
1078
    public static double parseDateTimeOrTimestamp(String timeStr, ZoneId zoneId) throws DateTimeParseException {
1079
        return FormattingAndParsingUtils.parseDateTimeOrTimestamp(timeStr, zoneId);
1080
    }
1081

1082
    /**
1083
     * Parses an ISO date with or without time or a timestamp (in milliseconds).
1084
     * Returns the date or timestamp converted to a timestamp in milliseconds.
1085
     * Default time zone is used (UTC).
1086
     *
1087
     * @param timeStr Date or timestamp string
1088
     * @return Timestamp
1089
     * @throws DateTimeParseException if the time cannot be parsed
1✔
1090
     */
1091
    public static double parseDateTimeOrTimestamp(String timeStr) throws DateTimeParseException {
1092
        return FormattingAndParsingUtils.parseDateTimeOrTimestamp(timeStr);
1093
    }
1094

1095
    /**
1096
     * Returns the string representation of the given timestamp.
1097
     *
1098
     * @param timestamp the time, in milliseconds
1099
     * @return formatted timestamp
1✔
1100
     */
1101
    public static String printTimestamp(double timestamp) {
1102
        return TIMESTAMP_PRINTER.format(timestamp);
1103
    }
1104

1105
    /**
1106
     * Returns the date's string representation of the given timestamp.
1107
     *
1108
     * @param timestamp time, in milliseconds
1109
     * @param zoneId time zone to use or null to use default time zone (UTC)
1110
     * @return formatted date
1✔
1111
     */
1✔
1112
    public static String printDate(double timestamp, ZoneId zoneId) {
1113
        if (Double.isInfinite(timestamp) || Double.isNaN(timestamp)) {
1✔
1114
            return printTimestamp(timestamp);
1115
        }
1116
        return printDate(Instant.ofEpochMilli((long) timestamp), zoneId);
1117
    }
1118

1119
    /**
1120
     * Returns the date's string representation of the given instant.
1121
     *
1122
     * @param instant instant to format
1123
     * @param zoneId time zone to use or null to use default time zone (UTC)
1124
     * @return formatted date
1✔
1125
     */
1✔
1126
    public static String printDate(Instant instant, ZoneId zoneId) {
1✔
1127
        DateTimeFormatter datePrinterByTimeZone = getDatePrinterByTimeZone(zoneId);
1128
        ZonedDateTime zonedDateTime = instant.atZone(datePrinterByTimeZone.getZone());
1129
        return zonedDateTime.format(datePrinterByTimeZone);
1130
    }
1131

1132
    /**
1133
     * Returns the date's string representation of the given timestamp. Default time
1134
     * zone is used (UTC).
1135
     *
1136
     * @param timestamp time, in milliseconds
1137
     * @return formatted date
1✔
1138
     */
1139
    public static String printDate(double timestamp) {
1140
        return printDate(timestamp, null);
1141
    }
1142

1143
    /**
1144
     * Returns the time's string representation of the given timestamp.
1145
     *
1146
     * @param timestamp time, in milliseconds
1147
     * @param zoneId time zone to use or null to use default time zone (UTC)
1148
     * @return formatted time
1✔
1149
     */
1✔
1150
    public static String printDateTime(double timestamp, ZoneId zoneId) {
1151
        if (Double.isInfinite(timestamp) || Double.isNaN(timestamp)) {
1✔
1152
            return printTimestamp(timestamp);
1153
        }
1154
        return printDateTime(Instant.ofEpochMilli((long) timestamp), zoneId);
1155
    }
1156

1157
    /**
1158
     * Returns the time's string representation of the given instant.
1159
     *
1160
     * @param instant instant to format
1161
     * @param zoneId time zone to use or null to use default time zone (UTC)
1162
     * @return formatted time
1✔
1163
     */
1✔
1164
    public static String printDateTime(Instant instant, ZoneId zoneId) {
1✔
1165
        DateTimeFormatter dateTimePrinterByTimeZone = getDateTimePrinterByTimeZone(zoneId);
1✔
1166
        ZonedDateTime zonedDateTime2 = instant.atZone(dateTimePrinterByTimeZone.getZone());
1167
        OffsetDateTime time = OffsetDateTime.from(zonedDateTime2);
1168
        return time.format(dateTimePrinterByTimeZone);
1169
    }
1170

1171
    /**
1172
     * Returns the time's string representation of the given timestamp. Default time
1173
     * zone is used (UTC).
1174
     *
1175
     * @param timestamp time, in milliseconds
1176
     * @return formatted time
1✔
1177
     */
1178
    public static String printDateTime(double timestamp) {
1179
        return printDateTime(timestamp, null);
1180
    }
1181

1182
    /**
1183
     * Returns the string representation of the given timestamp in the given format.
1184
     *
1185
     * @param timestamp time, in milliseconds
1186
     * @param timeFormat time format
1187
     * @param zoneId time zone to use or null to use default time zone (UTC).
1188
     * @return formatted timestamp
1✔
1189
     */
1190
    public static String printTimestampInFormat(double timestamp, TimeFormat timeFormat, ZoneId zoneId) {
1✔
1191
        switch (timeFormat) {
1192
            case DATE:
1✔
1193
                return AttributeUtils.printDate(timestamp, zoneId);
1194
            case DATETIME:
1✔
1195
                return AttributeUtils.printDateTime(timestamp, zoneId);
1196
            case DOUBLE:
UNCOV
1197
                return AttributeUtils.printTimestamp(timestamp);
×
1198
        }
1199

1200
        throw new UnsupportedOperationException("Unknown TimeFormat");
1201
    }
1202

1203
    /**
1204
     * Returns the string representation of the given timestamp in the given format.
1205
     * Default time zone is used (UTC).
1206
     *
1207
     * @param timestamp time, in milliseconds
1208
     * @param timeFormat time format
UNCOV
1209
     * @return formatted timestamp
×
1210
     */
1211
    public static String printTimestampInFormat(double timestamp, TimeFormat timeFormat) {
1212
        return printTimestampInFormat(timestamp, timeFormat, null);
1213
    }
1214

1215
    /**
1216
     * Returns the string representation of the given array. The used format is the
1217
     * same format supported by {@link #parse(java.lang.String, java.lang.Class)}
1218
     * method
1219
     *
1220
     * @param arr Input array. Can be an array of objects or primitives.
1221
     * @return formatted array
1✔
1222
     */
1223
    public static String printArray(Object arr) {
1224
        return FormattingAndParsingUtils.printArray(arr);
1225
    }
1226

1227
    /**
1228
     * Returns true if the given column is a node column.
1229
     *
1230
     * @param colum column to test
1231
     * @return true if the column is a node column, false otherwise
1✔
1232
     */
1233
    public static boolean isNodeColumn(Column colum) {
1234
        return colum.getTable().getElementClass().equals(Node.class);
1235
    }
1236

1237
    /**
1238
     * Returns true if the given column is an edge column.
1239
     *
1240
     * @param colum column to test
1241
     * @return true if the column is an edge column, false otherwise
1✔
1242
     */
1243
    public static boolean isEdgeColumn(Column colum) {
1244
        return colum.getTable().getElementClass().equals(Edge.class);
1245
    }
1246

1247
    /**
1248
     * Returns a copy of the provided object.
1249
     * <p>
1250
     * The copy is a deep copy for arrays, {@link IntervalSet},
1251
     * {@link TimestampSet}, sets and lists
1252
     *
1253
     * @param obj object to copy
1254
     * @return copy of the provided object
1✔
1255
     */
1✔
1256
    public static Object copy(Object obj) {
1257
        if (obj == null) {
1✔
1258
            return null;
1✔
UNCOV
1259
        }
×
1260
        Class typeClass = obj.getClass();
1261
        if (!isSupported(typeClass)) {
1✔
1262
            throw new IllegalArgumentException("Unsupported type " + typeClass.getCanonicalName());
1✔
1263
        }
1264
        typeClass = getStandardizedType(typeClass);
1265
        obj = standardizeValue(obj);
1✔
1266

1✔
1267
        // Primitive
1268
        if (isSimpleType(typeClass)) {
1269
            return obj;
1270
        }
1✔
1271

1✔
1272
        // Instant
1273
        if (typeClass.equals(Instant.class)) {
1274
            return obj;
1275
        }
1✔
1276

1✔
1277
        // Interval types:
1✔
1278
        if (typeClass.equals(IntervalSet.class)) {
1✔
1279
            return new IntervalSet((IntervalSet) obj);
1✔
1280
        } else if (typeClass.equals(IntervalStringMap.class)) {
1✔
1281
            return new IntervalStringMap((IntervalStringMap) obj);
1✔
1282
        } else if (typeClass.equals(IntervalByteMap.class)) {
1✔
1283
            return new IntervalByteMap((IntervalByteMap) obj);
1✔
1284
        } else if (typeClass.equals(IntervalShortMap.class)) {
1✔
1285
            return new IntervalShortMap((IntervalShortMap) obj);
1✔
1286
        } else if (typeClass.equals(IntervalIntegerMap.class)) {
1✔
1287
            return new IntervalIntegerMap((IntervalIntegerMap) obj);
1✔
1288
        } else if (typeClass.equals(IntervalLongMap.class)) {
1✔
1289
            return new IntervalLongMap((IntervalLongMap) obj);
1✔
1290
        } else if (typeClass.equals(IntervalFloatMap.class)) {
1✔
1291
            return new IntervalFloatMap((IntervalFloatMap) obj);
1✔
1292
        } else if (typeClass.equals(IntervalDoubleMap.class)) {
1✔
1293
            return new IntervalDoubleMap((IntervalDoubleMap) obj);
1✔
1294
        } else if (typeClass.equals(IntervalBooleanMap.class)) {
1✔
1295
            return new IntervalBooleanMap((IntervalBooleanMap) obj);
1296
        } else if (typeClass.equals(IntervalCharMap.class)) {
1297
            return new IntervalCharMap((IntervalCharMap) obj);
1298
        }
1✔
1299

1✔
1300
        // Timestamp types:
1✔
1301
        if (typeClass.equals(TimestampSet.class)) {
1✔
1302
            return new TimestampSet((TimestampSet) obj);
1✔
1303
        } else if (typeClass.equals(TimestampStringMap.class)) {
1✔
1304
            return new TimestampStringMap((TimestampStringMap) obj);
1✔
1305
        } else if (typeClass.equals(TimestampByteMap.class)) {
1✔
1306
            return new TimestampByteMap((TimestampByteMap) obj);
1✔
1307
        } else if (typeClass.equals(TimestampShortMap.class)) {
1✔
1308
            return new TimestampShortMap((TimestampShortMap) obj);
1✔
1309
        } else if (typeClass.equals(TimestampIntegerMap.class)) {
1✔
1310
            return new TimestampIntegerMap((TimestampIntegerMap) obj);
1✔
1311
        } else if (typeClass.equals(TimestampLongMap.class)) {
1✔
1312
            return new TimestampLongMap((TimestampLongMap) obj);
1✔
1313
        } else if (typeClass.equals(TimestampFloatMap.class)) {
1✔
1314
            return new TimestampFloatMap((TimestampFloatMap) obj);
1✔
1315
        } else if (typeClass.equals(TimestampDoubleMap.class)) {
1✔
1316
            return new TimestampDoubleMap((TimestampDoubleMap) obj);
1✔
1317
        } else if (typeClass.equals(TimestampBooleanMap.class)) {
1✔
1318
            return new TimestampBooleanMap((TimestampBooleanMap) obj);
1319
        } else if (typeClass.equals(TimestampCharMap.class)) {
1320
            return new TimestampCharMap((TimestampCharMap) obj);
1321
        }
1✔
1322

1✔
1323
        // Array
1✔
1324
        if (isArrayType(typeClass)) {
1✔
1325
            Class componentType = typeClass.getComponentType();
1✔
1326
            int length = Array.getLength(obj);
1✔
1327
            Object dest = Array.newInstance(componentType, length);
1328
            System.arraycopy(obj, 0, dest, 0, length);
1329
            return dest;
1330
        }
1✔
UNCOV
1331

×
1332
        // List
1✔
UNCOV
1333
        if (obj instanceof CharArrayList) {
×
1334
            return new CharArrayList((CharArrayList) obj);
1✔
UNCOV
1335
        } else if (obj instanceof BooleanArrayList) {
×
1336
            return new BooleanArrayList((BooleanArrayList) obj);
1✔
UNCOV
1337
        } else if (obj instanceof ByteArrayList) {
×
1338
            return new ByteArrayList((ByteArrayList) obj);
1✔
UNCOV
1339
        } else if (obj instanceof ShortArrayList) {
×
1340
            return new ShortArrayList((ShortArrayList) obj);
1✔
UNCOV
1341
        } else if (obj instanceof IntArrayList) {
×
1342
            return new IntArrayList((IntArrayList) obj);
1✔
UNCOV
1343
        } else if (obj instanceof LongArrayList) {
×
1344
            return new LongArrayList((LongArrayList) obj);
1✔
UNCOV
1345
        } else if (obj instanceof FloatArrayList) {
×
1346
            return new FloatArrayList((FloatArrayList) obj);
1✔
1347
        } else if (obj instanceof DoubleArrayList) {
1✔
1348
            return new DoubleArrayList((DoubleArrayList) obj);
1349
        } else if (obj instanceof ObjectArrayList) {
1350
            return new ObjectArrayList((ObjectArrayList) obj);
1351
        }
1✔
UNCOV
1352

×
1353
        // Map
1✔
UNCOV
1354
        if (obj instanceof Char2ObjectOpenHashMap) {
×
1355
            return new Char2ObjectOpenHashMap((Char2ObjectOpenHashMap) obj);
1✔
UNCOV
1356
        } else if (obj instanceof Byte2ObjectOpenHashMap) {
×
1357
            return new Byte2ObjectOpenHashMap((Byte2ObjectOpenHashMap) obj);
1✔
UNCOV
1358
        } else if (obj instanceof Short2ObjectOpenHashMap) {
×
1359
            return new Short2ObjectOpenHashMap((Short2ObjectOpenHashMap) obj);
1✔
UNCOV
1360
        } else if (obj instanceof Int2ObjectOpenHashMap) {
×
1361
            return new Int2ObjectOpenHashMap((Int2ObjectOpenHashMap) obj);
1✔
UNCOV
1362
        } else if (obj instanceof Long2ObjectOpenHashMap) {
×
1363
            return new Long2ObjectOpenHashMap((Long2ObjectOpenHashMap) obj);
1✔
UNCOV
1364
        } else if (obj instanceof Float2ObjectOpenHashMap) {
×
1365
            return new Float2ObjectOpenHashMap((Float2ObjectOpenHashMap) obj);
1✔
1366
        } else if (obj instanceof Double2ObjectOpenHashMap) {
1✔
1367
            return new Double2ObjectOpenHashMap((Double2ObjectOpenHashMap) obj);
1368
        } else if (obj instanceof Object2ObjectOpenHashMap) {
1369
            return new Object2ObjectOpenHashMap((Object2ObjectOpenHashMap) obj);
1370
        }
1✔
UNCOV
1371

×
1372
        // Set
1✔
UNCOV
1373
        if (obj instanceof CharOpenHashSet) {
×
1374
            return new CharOpenHashSet((CharOpenHashSet) obj);
1✔
UNCOV
1375
        } else if (obj instanceof BooleanOpenHashSet) {
×
1376
            return new BooleanOpenHashSet((BooleanOpenHashSet) obj);
1✔
UNCOV
1377
        } else if (obj instanceof ByteOpenHashSet) {
×
1378
            return new ByteOpenHashSet((ByteOpenHashSet) obj);
1✔
UNCOV
1379
        } else if (obj instanceof ShortOpenHashSet) {
×
1380
            return new ShortOpenHashSet((ShortOpenHashSet) obj);
1✔
UNCOV
1381
        } else if (obj instanceof IntOpenHashSet) {
×
1382
            return new IntOpenHashSet((IntOpenHashSet) obj);
1✔
UNCOV
1383
        } else if (obj instanceof LongOpenHashSet) {
×
1384
            return new LongOpenHashSet((LongOpenHashSet) obj);
1✔
UNCOV
1385
        } else if (obj instanceof FloatOpenHashSet) {
×
1386
            return new FloatOpenHashSet((FloatOpenHashSet) obj);
1✔
1387
        } else if (obj instanceof DoubleOpenHashSet) {
1✔
1388
            return new DoubleOpenHashSet((DoubleOpenHashSet) obj);
1389
        } else if (obj instanceof ObjectOpenHashSet) {
UNCOV
1390
            return new ObjectOpenHashSet((ObjectOpenHashSet) obj);
×
1391
        }
1392

1393
        return obj;
1394
    }
1395
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc