1 package org.djutils.data.serialization;
2
3 import org.djunits.value.vdouble.scalar.base.DoubleScalar;
4 import org.djunits.value.vfloat.scalar.base.FloatScalar;
5 import org.djutils.data.Column;
6 import org.djutils.exceptions.Throw;
7
8 /**
9 * TextSerializer defines the serialize and deserialize methods.
10 * <p>
11 * Copyright (c) 2020-2025 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
12 * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
13 * </p>
14 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
15 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
16 * @author <a href="https://dittlab.tudelft.nl">Wouter Schakel</a>
17 * @param <T> the value type
18 */
19 public interface TextSerializer<T>
20 {
21 /**
22 * Serialize a value to text in such a way that it can be deserialized with the corresponding deserializer. Note that
23 * {@code null} values for value <b>are allowed</b>. A {@code null} values stands for an empty column value in a CVS-file, a
24 * missing tag in an XML-file, etc.
25 * @param value the value to serialize, may be {@code null}
26 * @param unit the unit used to convert the data to and store, so all valus in a column may have the same unit. The
27 * value may be {@code null} or blank
28 * @return a string representation of the value that can later be deserialized, or {@code null}to denote a missing
29 * value
30 */
31 String serialize(T value, String unit);
32
33 /**
34 * Deserialize a value from text that has been created with the corresponding serializer. Note that {@code null} values for
35 * text <b>are allowed</b>. A {@code null} values stands for an empty column value in a CVS-file, a missing tag in an
36 * XML-file, etc. In this way, we can explicitly show values that were not specified in the file. Also, the type may be
37 * {@code null}; this is, for instance, the case for any {@code TextSerializer} implementing {@code
38 * SpecificTextSerializer}, where no class needs to be provided (although it can).
39 * @param type class of the value type, may be {@code null}
40 * @param text the string to deserialize, may be {@code null} or blank
41 * @param unit unit with the value, may be {@code null} or blank
42 * @return an instance of the object created with the corresponding serializer, may be {@code null} when a value was not
43 * specified in the source from which the deserializer was called
44 */
45 T deserialize(Class<T> type, String text, String unit);
46
47 /**
48 * Resolve the correct (de)serializer for the given class, and return an instance of the (de)serializer.
49 * @param valueClass the class to resolve the (de)serializer for
50 * @return an instance of the correct (de)serializer
51 * @throws TextSerializationException when there is no corresponding (de)serializer for the class
52 */
53 static TextSerializer<?> resolve(final Class<?> valueClass) throws TextSerializationException
54 {
55 Throw.whenNull(valueClass, "valueClass cannot be null");
56 if (valueClass.isPrimitive())
57 {
58 if (valueClass.equals(int.class))
59 {
60 return new IntegerSerializer();
61 }
62 else if (valueClass.equals(double.class))
63 {
64 return new DoubleSerializer();
65 }
66 else if (valueClass.equals(float.class))
67 {
68 return new FloatSerializer();
69 }
70 else if (valueClass.equals(long.class))
71 {
72 return new LongSerializer();
73 }
74 else if (valueClass.equals(short.class))
75 {
76 return new ShortSerializer();
77 }
78 else if (valueClass.equals(byte.class))
79 {
80 return new ByteSerializer();
81 }
82 else if (valueClass.equals(boolean.class))
83 {
84 return new BooleanSerializer();
85 }
86 else if (valueClass.equals(char.class))
87 {
88 return new CharacterSerializer();
89 }
90 }
91
92 else if (Number.class.isAssignableFrom(valueClass))
93 {
94 if (valueClass.equals(Integer.class))
95 {
96 return new IntegerSerializer();
97 }
98 else if (valueClass.equals(Double.class))
99 {
100 return new DoubleSerializer();
101 }
102 else if (valueClass.equals(Float.class))
103 {
104 return new FloatSerializer();
105 }
106 else if (valueClass.equals(Long.class))
107 {
108 return new LongSerializer();
109 }
110 else if (valueClass.equals(Short.class))
111 {
112 return new ShortSerializer();
113 }
114 else if (valueClass.equals(Byte.class))
115 {
116 return new ByteSerializer();
117 }
118 else if (DoubleScalar.class.isAssignableFrom(valueClass)) // DoubleScalar is a Number
119 {
120 return new DoubleScalarSerializer<>();
121 }
122 else if (FloatScalar.class.isAssignableFrom(valueClass)) // FloatScalar is a Number
123 {
124 return new FloatScalarSerializer<>();
125 }
126 }
127
128 else if (valueClass.equals(Boolean.class))
129 {
130 return new BooleanSerializer();
131 }
132
133 else if (valueClass.equals(Character.class))
134 {
135 return new CharacterSerializer();
136 }
137
138 else if (valueClass.equals(String.class))
139 {
140 return new StringSerializer();
141 }
142
143 throw new TextSerializationException("Cannot resolve the Text(de)serializer for class " + valueClass.getName());
144 }
145
146 /**
147 * Helper function to deal with casting when calling {@code TextSerializer.serialize()}. When the {@code resolve(class)}
148 * method returns an 'unspecified' serializer, this {@code serialize} method allows you to use it. Note that {@code null}
149 * values for value <b>are allowed</b>. A {@code null} values stands for an empty column value in a CVS-file, a missing tag
150 * in an XML-file, etc.
151 * @param <T> value type
152 * @param serializer serializer
153 * @param value value, may be {@code null}
154 * @param unit the unit used to convert the data to and store, so all valus in a column may have the same unit. The
155 * value may be {@code null} or blank
156 * @return serialized value, or {@code null}to denote a missing value
157 */
158 @SuppressWarnings("unchecked")
159 static <T> String serialize(final TextSerializer<?> serializer, final Object value, final String unit)
160 {
161 return ((TextSerializer<T>) serializer).serialize((T) value, unit);
162 }
163
164 /**
165 * Helper function to deal with casting when calling {@code TextSerializer.deserialize()}. When the {@code resolve(class)}
166 * method returns an 'unspecified' serializer, this {@code serialize} method allows you to use it. Note that {@code null}
167 * values for text <b>are allowed</b>. A {@code null} values stands for an empty column value in a CVS-file, a missing tag
168 * in an XML-file, etc. In this way, we can explicitly show values that were not specified in the file for a certain column.
169 * @param <T> value type
170 * @param serializer serializer
171 * @param text value, may be {@code null}
172 * @param column columns
173 * @return deserialized value, may be {@code null} when a value was not specified in the source for which the
174 * deserializer was called
175 */
176 @SuppressWarnings("unchecked")
177 static <T> T deserialize(final TextSerializer<?> serializer, final String text, final Column<?> column)
178 {
179 return ((TextSerializer<T>) serializer).deserialize((Class<T>) column.getValueType(), text, column.getUnit());
180 }
181
182 }