View Javadoc
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-2024 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 T; the value to serialize, may be {@code null}
26       * @param unit String; 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 String; 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&lt;T&gt;; class of the value type, may be {@code null}
40       * @param text String; the string to deserialize, may be {@code null} or blank
41       * @param unit String; unit with the value, may be {@code null} or blank
42       * @return T; 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 Class&lt;?&gt;; 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 TextSerializer&lt;?&gt;; serializer
153      * @param value Object; value, may be {@code null}
154      * @param unit String; 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 String; 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 TextSerializer&lt;?&gt;; serializer
171      * @param text String; value, may be {@code null}
172      * @param column Column&lt;?&gt;; columns
173      * @return T; 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 }