Class FieldType<T>

  • Type Parameters:
    T - The associated Java type
    All Implemented Interfaces:
    Serializable, Comparator<T>
    Direct Known Subclasses:
    NonNullFieldType, NullSafeType

    public abstract class FieldType<T>
    extends Object
    implements Comparator<T>, Serializable
    Defines the encoding, ordering, and range of possible values for a SimpleField.

    A FieldType maps between instances of its supported Java type and the self-delimited byte[] encoding of those instances used in the database. The byte[] encoding also implicitly defines the database sort order (via unsigned lexicographical ordering). This ordering is also reflected via compare().

    A FieldType also defines a mapping between Java instances and String values. There are two separate String forms, a regular form and a self-delimiting form.

    FieldTypes have these requirements and properties:

    • They have a unique name; typically the same as their supported type.
    • All possible values can be represented in Java as an instance of the associated Java type (possibly including null).
    • Instances totally order their Java values. If the associated Java type implements Comparable, then the two orderings do not necessarily have to agree, but they should if possible.
    • All possible values can be encoded/decoded into a self-delimiting binary string (i.e., byte[] array) without losing information, and these binary strings, when sorted lexicographically using unsigned comparison, sort consistently with the total ordering of the corresponding Java values.
    • All possible values can be encoded/decoded to/from Strings without losing information, with both a regular string form for non-null values and a self-delimiting string form for any value including null (these two forms may be the same).
    • null may or may not be a supported value; if so, it must be handled by compare() (typically null values sort last) and have binary and string encodings just like any other value.
    • There is a default value. For types that support null, the default value must be null.
    • An optional encoding signature protects against incompatible encodings when a FieldType's binary or string encoding changes without changing the name.

    Two FieldType instances should be equal according to equals() if only if they behave identically with respect to all of the above.

    A FieldTypeRegistry contains a registry of FieldTypes indexed by name.

    Instances are Serializable if their default values are (typically the default value is null, making this the case).

    See Also:
    FieldTypeRegistry, Serialized Form
    • Field Detail

      • NAME_PATTERN

        public static final String NAME_PATTERN
        The regular expression that FieldType names must match. This pattern is the same as is required for Java identifiers, except that the following additional characters are allowed after the first character: dot (`.') and dash (`-'), and lastly up to 255 pairs of square brackets (`[]') to indicate an array type.
        See Also:
        Constant Field Values
      • name

        protected final String name
      • typeToken

        protected final TypeToken<T> typeToken
      • signature

        protected final long signature
    • Constructor Detail

      • FieldType

        protected FieldType​(String name,
                            TypeToken<T> typeToken,
                            long signature,
                            T defaultValue)
        Constructor.
        Parameters:
        name - the name of this type
        typeToken - Java type for the field's values
        signature - binary encoding signature
        defaultValue - default value for this type
        Throws:
        IllegalArgumentException - if any parameter is null
        IllegalArgumentException - if name is invalid
      • FieldType

        protected FieldType​(Class<T> type,
                            long signature,
                            T defaultValue)
        Constructor taking a Class object. The name of this instance will be the name of the given class.
        Parameters:
        type - Java type for the field's values
        signature - binary encoding signature
        defaultValue - default value for this type
        Throws:
        NullPointerException - if type is null
    • Method Detail

      • getTypeToken

        public TypeToken<T> getTypeToken()
        Get the Java type corresponding to this type's values.
        Returns:
        this type's Java type
      • getEncodingSignature

        public long getEncodingSignature()
        Get the binary encoding signature of this type.

        The binary encoding signature is analogous to the serialVersionUID used by Java serialization. It represents a specific binary and/or String encoding for Java values. In the case that a FieldType implementation changes its binary encoding, but not it's name, it must use a new, different binary encoding signature to eliminate the possibility of mixing incompatible encodings in software vs. persistent storage. Typically a value of zero is used until if/when such a change occurs.

        Note that another option when encodings change is simply to change the name of the type any case, the combination of FieldType name and signature must be unique in a FieldTypeRegistry.

        Returns:
        binary encoding signature
        See Also:
        JField.type(), JField.typeSignature()
      • write

        public abstract void write​(ByteWriter writer,
                                   T value)
        Write a value to the given output.
        Parameters:
        writer - byte output
        value - value to write (possibly null)
        Throws:
        IllegalArgumentException - if value is null and this type does not support null
        IllegalArgumentException - if writer is null
      • getDefaultValue

        public final byte[] getDefaultValue()
        Get the default value for this field type encoded as a byte[] array.
        Returns:
        encoded default value
      • getDefaultValueObject

        public final T getDefaultValueObject()
        Get the default value for this field type.
        Returns:
        default value
      • toParseableString

        public abstract String toParseableString​(T value)
        Encode a possibly null value as a String for later decoding by fromParseableString(). The string value must be self-delimiting, i.e., decodable even when followed by arbitrary additional characters, and must not start with whitespace or closing square bracket ("]").

        In addition, each of the characters in the returned String must be one of the valid XML characters (tab, newline, carriage return, \u0020 - \ud7ff, and \ue000 - \fffdf).

        Parameters:
        value - actual value (possibly null)
        Returns:
        string encoding of value acceptable to fromParseableString()
        Throws:
        IllegalArgumentException - if value is null and this type does not support null
        See Also:
        The XML 1.0 Specification
      • fromParseableString

        public abstract T fromParseableString​(ParseContext context)
        Parse a value previously encoded by toParseableString() as a self-delimited String and positioned at the start of the given parsing context.
        Parameters:
        context - parse context starting with a string previously encoded via toParseableString()
        Returns:
        actual value (possibly null)
        Throws:
        IllegalArgumentException - if the input is invalid
      • convert

        public <S> T convert​(FieldType<S> type,
                             S value)
        Attempt to convert a value from the given FieldType into a value of this FieldType.

        For a non-null value, the implementation in FieldType first checks whether the value is already a valid value for this type; if so, the value is returned. Otherwise, it invokes type.toString(value) to convert value into a String, and then attempts to parse that string via this.fromString(); if the parse fails, an IllegalArgumentException is thrown.

        If value is null, the implementation in FieldType returns null, unless this type does not support null values, in which case an IllegalArgumentException is thrown.

        Special handling also exists for certain conversions between built-in types:

        • Primitive types other than Boolean convert as if by the corresponding Java cast
        • Non-Boolean primitive types convert to Boolean as if by value != 0
        • Boolean converts to non-Boolean primitive types by first converting to zero (if false) or one (if true)
        • A char and a String of length one are convertible (other Strings are not)
        • A char[] array and a String are convertible
        • Arrays are converted by converting each array element individually (if possible)
        Type Parameters:
        S - source field type
        Parameters:
        type - the FieldType of value
        value - the value to convert
        Returns:
        value converted to this instance's type
        Throws:
        IllegalArgumentException - if the conversion fails
      • validate

        public T validate​(Object obj)
        Verify the given object is a valid instance of this FieldType's Java type and cast it to that type.

        Note that this method must throw IllegalArgumentException, not ClassCastException or NullPointerException, if obj does not have the correct type, or is an illegal null value.

        This method is allowed to perform widening conversions of the object that lose no information, e.g., from Integer to Long.

        The implementation in FieldType simply casts the value using this instance's raw Java type. Subclasses should override this method to implement any other restrictions, e.g., disallowing null values.

        Parameters:
        obj - object to validate
        Returns:
        obj cast to this field's type
        Throws:
        IllegalArgumentException - if obj in not of type T
        IllegalArgumentException - if obj is null and this type does not support null values
        IllegalArgumentException - if obj is in any other way not supported by this FieldType
      • compare

        public abstract int compare​(T value1,
                                    T value2)
        Order two field values.

        This method must provide a total ordering of all supported Java values that is consistent with the database ordering, i.e., the unsigned lexicographical ordering of the corresponding byte[] encoded field values.

        If null is a supported Java value, then the returned Comparator must accept null parameters without throwing an exception (note, this is a stronger requirement than the Comparator interface normally requires).

        Note: by convention, null values usually sort last.

        Specified by:
        compare in interface Comparator<T>
        Throws:
        IllegalArgumentException - if value1 or value2 is null and this type does not support null
      • hasPrefix0x00

        public boolean hasPrefix0x00()
        Determine whether any of this field type's encoded values start with a 0x00 byte. Certain optimizations are possible when this is not the case. It is safe for this method to always return true.

        Note: changing the return value of this method usually means changing the binary encoding, resulting in an incompatible type.

        The implementation in FieldType returns true.

        Returns:
        true if an encoded value starting with 0x00 exists
      • hasPrefix0xff

        public boolean hasPrefix0xff()
        Determine whether any of this field type's encoded values start with a 0xff byte. Certain optimizations are possible when this is not the case. It is safe for this method to always return true.

        Note: changing the return value of this method usually means changing the binary encoding, resulting in an incompatible type.

        The implementation in FieldType returns true.

        Returns:
        true if an encoded value starting with 0xff exists
      • genericizeForIndex

        public FieldType<T> genericizeForIndex()
        Remove any information that may differ between instances associated with the same indexed field in the same schema.

        This operation should be applied before using this instance with index queries.

        Returns:
        this instance with all non-index-relevant information elided
      • getKeyRange

        public KeyRange getKeyRange​(Bounds<? extends T> bounds)
        Calculate the KeyRange that includes exactly those encoded values that lie within the given bounds.
        Parameters:
        bounds - bounds to impose
        Returns:
        KeyRange corresponding to bounds
        Throws:
        IllegalArgumentException - if bounds is null
      • hashCode

        public int hashCode()
        Overrides:
        hashCode in class Object