Object converters

There is sometimes a need to convert instances from a source Java type to a target Java type while those types are unknown at compile time. Various projects (Apache Common Convert, Spring, etc.) have created their own interface for performing object conversions between types known only at runtime. Details vary, but such interfaces typically look like below:

interface ObjectConverter<S,T> {   // Some projects use only "Converter" as interface name.
    T apply(S object);             // Another method name commonly found in other projects is "convert".
}

Like other projects, Apache SIS also defines its own ObjectConverter interface. The main difference between SIS converter interface and the interfaces found in other projects is that SIS converters provide some information about their mathematical properties. An Apache SIS converter can have zero, one or many of the following properties:

Injective
A function is injective if no pair of S values can produce the same T value.

Example: the IntegerString conversion performed by Integer.toString() is an injective function because if two Integer values are not equal, then it is guaranteed that their conversions will result in different String values. However the StringInteger conversion performed by Integer.valueOf(String) is not an injective function because many distinct String values can be converted to the same Integer value. For example converting the "42", "+42" and "0042" character strings all result in the same 42 integer value.

Surjective
A function is surjective if each values of T can be created from at least one value of S.

Example: the StringInteger conversion performed by Integer.valueOf(String) is a surjective function because every Integer value can be created from at least one String value. However the IntegerString conversion performed by Integer.toString() is not a surjective function because it cannot produce all possible String values. For example there is no way to produce the "ABC" value with the Integer.toString() method.

Bijective
A function is bijective if there is a one-to-one relationship between S and T values.

Note: the bijective property is defined here for clarity, but actually does not have an explicit item in Apache SIS FunctionProperty enumeration. It is not necessary since a function that is both injective and surjective is necessarily bijective.

Order preserving
A function is order preserving if any sequence of increasing S values is mapped to a sequence of increasing T values.

Example: conversion from Integer to Long preserve the natural ordering of elements. However conversions from Integer to String do not preserve natural ordering, because some sequences of increasing integer values are ordered differently when their string representations are sorted by lexicographic order. For example 1, 2, 10 become "1", "10", "2".

Order reversing
A function is order reversing if any sequence of increasing S values is mapped to a sequence of decreasing T values.

Example: a conversion that reverses the sign of numbers.

Above information may seem unnecessary when values are converted without taking in account the context in which the values appear. But when the value to convert is part of a bigger object, then above information can affect the way the converted value will be used. For example conversion of a [minmax] range is straightforward when the converter is order preserving. But if the converter is order reversing, then the minimum and maximum values need to be interchanged. For example if the converter reverses the sign of values, then the converted range is [-max … -min]. If the converter is neither order preserving or order reversing, then range conversion is not allowed at all (because it does not contain the same set of values) even if the minimum and maximum values could be converted individually.