DoubleScalarSerializer.java

package org.djutils.data.serialization;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;

import org.djunits.unit.Unit;
import org.djunits.value.vdouble.scalar.base.DoubleScalarInterface;

/**
 * ScalarSerializer (de)serializes DJUNITS scalars. <br>
 * <br>
 * Copyright (c) 2020-2020 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
 * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
 * distributed under a three-clause BSD-style license, which can be found at
 * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. <br>
 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
 * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
 * @param <U> the unit type
 * @param <S> the scalar type
 */
public class DoubleScalarSerializer<U extends Unit<U>, S extends DoubleScalarInterface<U, S>> implements TextSerializer<S>
{
    /** cache of the retrieved valueOf(String) methods for scalars based on the stored string. */
    private static Map<String, Method> valueOfMethodCache = new LinkedHashMap<>();

    /**
     * Serialize a DoubleScalar value to text in such a way that it can be deserialized with the corresponding deserializer. In
     * this case, it serializes the class name of the Scalar, followed by a hash sign, followed by the printed value of the
     * scalar including the unit. So a Length of 12.5 kilometer will return "org.djunits.value.vdouble.scalar.Length#12.5 km".
     * @param value S; the scalar to serialize
     * @return String; a string representation of the value that can later be deserialized
     */
    @SuppressWarnings("unchecked")
    @Override
    public String serialize(final Object value)
    {
        return value.getClass().getName() + "#" + ((S) value).toTextualString();
    }

    /**
     * Deserialize a String to the correct DoubleScalar value in line with the DoubleScalarSerializer.serialize method. In this
     * case, it assumes a string with the class name of the Scalar, followed by a hash sign, followed by the printed value of
     * the scalar including the unit. So, the String "org.djunits.value.vdouble.scalar.Length#12.5 km" will be deserialized into
     * a Length of 12500 m with LengthUnit.KILOMETER as the display unit. The method caches the valueOf(String) method for
     * repeated use.
     * @param text text; the text to deserialize
     * @return S; the reconstructed scalar
     */
    @SuppressWarnings("unchecked")
    @Override
    public S deserialize(final String text)
    {
        String[] parts = text.split("#");
        try
        {
            Method valueOfMethod = valueOfMethodCache.get(parts[0]);
            if (valueOfMethod == null)
            {
                Class<?> scalarClass = Class.forName(parts[0]);
                valueOfMethod = scalarClass.getDeclaredMethod("valueOf", String.class);
                valueOfMethodCache.put(parts[0], valueOfMethod);
            }
            return (S) valueOfMethodCache.get(parts[0]).invoke(null, parts[1]);
        }
        catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
                | NoSuchMethodException | SecurityException exception)
        {
            throw new RuntimeException(exception);
        }
    }

}