Module org.apache.sis.util
Package org.apache.sis.io

# Class CompoundFormat<T>

Object
Format
CompoundFormat<T>

Type Parameters:
  `T` - the base type of objects parsed and formatted by this class.

All Implemented Interfaces:
  `Serializable`, `Cloneable`, `Localized`

Direct Known Subclasses:
  `CoordinateFormat`, `TabularFormat`, `WKTFormat`

public abstract class CompoundFormat<T> extends Format implements Localized
Base class of `Format` implementations which delegate part of their work to other `Format` instances. `CompoundFormat` subclasses typically work on relatively large blocks of data, for example a metadata tree or a Well Known Text (WKT). Those blocks of data usually contain smaller elements like numbers and dates, whose parsing and formatting can be delegated to `NumberFormat` and `DateFormat` respectively. Subclasses can obtain instances of those formats by call to `getFormat(Class)` where the argument is the type of the value to parse or format. `CompoundFormat` supports at least the following value types, but subclasses may add more types:

Supported value types
Value type | Format type | Remarks
--- | --- | ---
`DirectPosition` | `CoordinateFormat` | Requires `org​.apache​.sis​.referencing` module.
`Angle` | `AngleFormat` |
`Date` | `DateFormat` | Timezone specified by `getTimeZone()`.
`Number` | `NumberFormat` |
`Unit` | `UnitFormat` |
`Range` | `RangeFormat` |
`Class` | (internal) |

## Sources and destinations

Since `CompoundFormat` may work on larger texts than the usual `Format` classes, it defines `parse` and `format` methods working with arbitrary `CharSequence` and `Appendable` instances. The standard `Format` methods redirect to the above-cited methods.

## Sub-classing

The abstract methods to be defined by subclasses are:

- `getValueType()`
- `format(Object, Appendable)`
- `parse(CharSequence, ParsePosition)`

## Comparison with other API

In the standard `Format` class, the `parse` methods either accept a `ParsePosition` argument and returns `null` on error, or does not take position argument and throws a `ParseException` on error. In this `CompoundFormat` class, the `parse` method both takes a `ParsePosition` argument and throws a `ParseException` on error. This allows both substring parsing and more accurate exception message in case of error.

Since:
  0.3
See Also:
  - Serialized Form

- ## Nested Class Summary

  ## Nested classes/interfaces inherited from class Format

  `Format​.Field`

- ## Constructor Summary

  Constructors
  Modifier
  Constructor
  Description
  `protected`
  `CompoundFormat(Locale locale, TimeZone timezone)`
  Creates a new format for the given locale.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `CompoundFormat<T>`
  `clone()`
  Returns a clone of this format.
  `protected Format`
  `createFormat(Class<?> valueType)`
  Creates a new format to use for parsing and formatting values of the given type.
  `StringBuffer`
  `format(Object object, StringBuffer toAppendTo, FieldPosition pos)`
  Writes a textual representation of the specified object in the given buffer.
  `abstract void`
  `format(T object, Appendable toAppendTo)`
  Writes a textual representation of the given object in the given stream or buffer.
  `protected Format`
  `getFormat(Class<?> valueType)`
  Returns the format to use for parsing and formatting values of the given type.
  `Locale`
  `getLocale()`
  Returns the locale used by this format.
  `Locale`
  `getLocale(Locale​.Category category)`
  Returns the locale for the given category.
  `TimeZone`
  `getTimeZone()`
  Returns the timezone used by this format.
  `abstract Class<? extends T>`
  `getValueType()`
  Returns the base type of values parsed and formatted by this `Format` instance.
  `abstract T`
  `parse(CharSequence text, ParsePosition pos)`
  Creates an object from the given character sequence.
  `T`
  `parseObject(String text)`
  Creates an object from the given string representation.
  `T`
  `parseObject(String text, ParsePosition pos)`
  Creates an object from the given string representation, or returns `null` if an error occurred while parsing.

  ### Methods inherited from class Format

  `format, formatToCharacterIterator`

  ### Methods inherited from class Object

  `equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Constructor Details

  - ### CompoundFormat

    protected CompoundFormat(Locale locale, TimeZone timezone)
    Creates a new format for the given locale. The given locale can be `null` or `Locale​.ROOT` if this format shall parse and format "unlocalized" strings. See `getLocale()` for more information about the `ROOT` locale.
    Parameters:
      `locale` - the locale for the new `Format`, or `null` for `Locale​.ROOT`.
      `timezone` - the timezone, or `null` for UTC.

- ## Method Details

  - ### getLocale

    public Locale getLocale()
    Returns the locale used by this format. The returned value may be `Locale​.ROOT` if this format does not apply any localization. The definition of "unlocalized string" is implementation-dependent, but some typical examples are:
    - Format `Number` instances using `toString()` instead of `NumberFormat`.
    - Format `Date` instances using the ISO pattern instead of the English one.
    Specified by:
      `getLocale` in interface `Localized`
    Returns:
      the locale of this `Format`, or `Locale​.ROOT` for unlocalized format.

  - ### getLocale

    public Locale getLocale(Locale.Category category)
    Returns the locale for the given category. Subclasses may override this method in order to assign different roles to the different locale categories. A typical (but not mandatory) mapping is:
    - `Locale​.Category​.FORMAT` specifies the locale to use for numbers, dates and angles formatting.
    - `Locale​.Category​.DISPLAY` specifies the locale to use for `CodeList` labels and `InternationalString` contents.
    For subclasses that do not override this method, the default implementation returns `getLocale()`.

    #### Example

    The ISO 19162 (Well Known Text) standard requires a number format similar to the one defined by `Locale​.ROOT` while it allows informative texts (remarks, etc.) to be formatted according the user's locale. Consequently, `WKTFormat` fixes (usually) the locale for `Category​.FORMAT` to `Locale​.ROOT` and let `Category​.DISPLAY` be any locale.
    Parameters:
      `category` - the category for which a locale is desired.
    Returns:
      the locale for the given category (never `null`).
    Since:
      0.4

  - ### getTimeZone

    public TimeZone getTimeZone()
    Returns the timezone used by this format.
    Returns:
      the timezone used for this format, or UTC for unlocalized format.

  - ### getValueType

    public abstract Class<? extends T> getValueType()
    Returns the base type of values parsed and formatted by this `Format` instance. The returned type may be a subclass of `<T>` if the format is configured in a way that restrict the kind value to be parsed.

    #### Examples

    - `StatisticsFormat` unconditionally returns `Statistics​.class`.
    - `TreeTableFormat` unconditionally returns `TreeTable​.class`.
    Returns:
      the base type of values parsed and formatted by this `Format` instance.

  - ### parse

    public abstract T parse(CharSequence text, ParsePosition pos) throws ParseException
    Creates an object from the given character sequence. The parsing begins at the index given by the `pos` argument. If parsing succeeds, then:
    - The `pos` index is updated to the index after the last successfully parsed character.
    - The parsed object is returned.
    If parsing fails, then:
    - The `pos` index is left unchanged
    - The `pos` error index is set to the beginning of the unparsable character sequence.
    - One of the following actions is taken (at implementation choice):
      - this method returns `null`, or
      - a `ParseException` is thrown with an error offset set to the index of the first unparsable character.
    Note: if a `ParseException` is thrown, its error offset is usually the same than the `ParsePosition` error index, but implementations are free to adopt a slightly different policy. For example if parsing of the `"30.0 40,0"` coordinate fails on the coma in the last number, then the `pos` error index may be set to 5 (the beginning of the `"40.0"` character sequence) or to 7 (the coma position), depending on the implementation.
    Most implementations never return `null`. However, some implementations may choose to return `null` if they can determine that the given text is not a supported format and reserve `ParseException` for the cases where the text seems to be the expected format but contains a malformed element.
    Parameters:
      `text` - the character sequence for the object to parse.
      `pos` - the position where to start the parsing. On return, the position where the parsing stopped or where an error occurred.
    Returns:
      the parsed object, or `null` if the text is not recognized.
    Throws:
      `ParseException` - if an error occurred while parsing the object.

  - ### parseObject

    public T parseObject(String text, ParsePosition pos)
    Creates an object from the given string representation, or returns `null` if an error occurred while parsing. The parsing begins at the index given by the `pos` argument. If parsing succeeds, then:
    - The `pos` index is updated to the index after the last successfully parsed character.
    - The parsed object is returned.
    If parsing fails, then:
    - The `pos` index is left unchanged
    - The `pos` error index is set to the index of the character where the error occurred.
    - `null` is returned.
    The default implementation delegates to `parse(CharSequence, ParsePosition)`.
    Specified by:
      `parseObject` in class `Format`
    Parameters:
      `text` - the string representation of the object to parse.
      `pos` - the position where to start the parsing.
    Returns:
      the parsed object, or `null` if the given string cannot be parsed.

  - ### parseObject

    public T parseObject(String text) throws ParseException
    Creates an object from the given string representation. The default implementation delegates to `parse(CharSequence, ParsePosition)` and ensures that the given string has been fully used, ignoring trailing spaces and ISO control characters.

    #### Whitespaces

    The usual SIS policy, as documented in the `CharSequences` class, is to test for whitespaces using the `Character​.isWhitespace(…)` method. The combination of `isSpaceChar(…)` and `isISOControl(…)` done in this `parseObject(…)` method is more permissive since it encompasses all whitespace characters, plus non-breaking spaces and non-white ISO controls.
    Overrides:
      `parseObject` in class `Format`
    Parameters:
      `text` - the string representation of the object to parse.
    Returns:
      the parsed object.
    Throws:
      `ParseException` - if an error occurred while parsing the object.

  - ### format

    public abstract void format(T object, Appendable toAppendTo) throws IOException
    Writes a textual representation of the given object in the given stream or buffer.
    Parameters:
      `object` - the object to format.
      `toAppendTo` - where to format the object.
    Throws:
      `IOException` - if an error occurred while writing to the given appendable.

  - ### format

    public StringBuffer format(Object object, StringBuffer toAppendTo, FieldPosition pos)
    Writes a textual representation of the specified object in the given buffer. This method delegates its work to `format(Object, Appendable)`, but without propagating `IOException`. The I/O exception should never occur since we are writing in a `StringBuffer`.
    Note: Strictly speaking, an `IOException` could still occur if a subclass overrides the above `format` method and performs some I/O operation outside the given `StringBuffer`. However, this is not the intended usage of this class and implementers should avoid such unexpected I/O operation.
    Specified by:
      `format` in class `Format`
    Parameters:
      `object` - the object to format.
      `toAppendTo` - where to format the object.
      `pos` - ignored in current implementation.
    Returns:
      the given buffer, returned for convenience.

  - ### getFormat

    protected Format getFormat(Class<?> valueType)
    Returns the format to use for parsing and formatting values of the given type. This method applies the following algorithm:
    1. If a format is cached for the given type, return that format.
    2. Otherwise if a format can be created for the given type, cache the newly created format and return it.
    3. Otherwise do again the same checks for the super class.
    4. If no format is found for a concrete class, search again for all implemented interfaces.
    5. If no format can be created, return `null`.
    See `createFormat(Class)` for the list of value types recognized by the default `CompoundFormat` implementation.
    Parameters:
      `valueType` - the base type of values to parse or format, or `null` if unknown.
    Returns:
      the format to use for parsing and formatting values of the given type or any parent type, or `null` if none.

  - ### createFormat

    protected Format createFormat(Class<?> valueType)
    Creates a new format to use for parsing and formatting values of the given type. This method is invoked by `getFormat(Class)` the first time that a format is needed for the given type. The class given in argument can be any of the classes listed in the "Value type" column below:

    Supported value types
    Value type | Format type
    --- | ---
    `DirectPosition` | `CoordinateFormat`
    `Angle` | `AngleFormat`
    `Date` | `DateFormat`
    `Number` | `NumberFormat`
    `Unit` | `UnitFormat`
    `Quantity` | `QuantityFormat`
    `Range` | `RangeFormat`
    `Class` | (internal)

    Subclasses can override this method for adding more types, or for configuring the newly created `Format` instances. Note that implementations shall check the type using the `expected == type` comparator, not `expected.isAssignableFrom(type)`, because the check for parent types is done by the `getFormat(Class)` method. This approach allows subclasses to create specialized formats for different value sub-types. For example, a subclass may choose to format `Double` values differently than other types of number.
    Parameters:
      `valueType` - the base type of values to parse or format.
    Returns:
      the format to use for parsing of formatting values of the given type, or `null` if none.

  - ### clone

    public CompoundFormat<T> clone()
    Returns a clone of this format.
    Overrides:
      `clone` in class `Format`
    Returns:
      a clone of this format.