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<T>; 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<?>; 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<?>; 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<?>; serializer 171 * @param text String; value, may be {@code null} 172 * @param column Column<?>; 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 }