Class CalendricalRule<T>

java.lang.Object
javax.time.calendar.CalendricalRule<T>
Type Parameters:
T - the underlying type representing the data, typically a Calendrical, Number or Enum, must be immutable, should be comparable
All Implemented Interfaces:
Serializable, Comparable<CalendricalRule<?>>, Comparator<Calendrical>
Direct Known Subclasses:
Chronology.Rule, CopticDate.Rule, DateTimeFieldRule, HistoricDate.Rule, ISOChronology.EpochDaysRule, ISOChronology.NanoOfDayRule, LocalDate.Rule, LocalDateTime.Rule, LocalTime.Rule, MonthDay.Rule, OffsetDate.Rule, OffsetDateTime.Rule, OffsetTime.Rule, TimeZone.Rule, YearMonth.Rule, ZonedDateTime.Rule, ZoneOffset.Rule

public abstract class CalendricalRule<T> extends Object implements Comparable<CalendricalRule<?>>, Comparator<Calendrical>, Serializable
A rule defining how a single well-defined calendrical element operates.

Calendrical rules may define fields like day-of-month, combinations like date-time, or other related types like time-zone.

Each rule uses an underlying type to represent the data. This is captured in the generic type of the rule. The underlying type is reified and made available via getReifiedType(). It is expected, but not enforced, that the underlying type is Comparable.

CalendricalRule is an abstract class and must be implemented with care to ensure other classes in the framework operate correctly. All instantiable subclasses must be final, immutable and thread-safe and must ensure serialization works correctly.

See Also:
  • Field Details

    • serialVersionUID

      private static final long serialVersionUID
      A serialization identifier for this class.
      See Also:
    • reified

      private final Class<T> reified
      The reified class for the generic type.
    • chronology

      private final Chronology chronology
      The chronology of the rule, not null.
    • id

      private final String id
      The id of the rule, not null.
    • name

      private final String name
      The name of the rule, not null.
    • periodUnit

      private final PeriodUnit periodUnit
      The period unit, not null.
    • periodRange

      private final PeriodUnit periodRange
      The period range, not null.
  • Constructor Details

    • CalendricalRule

      protected CalendricalRule(Class<T> reifiedClass, Chronology chronology, String name, PeriodUnit periodUnit, PeriodUnit periodRange)
      Constructor used to create a rule.
      Parameters:
      reifiedClass - the reified class, not null
      chronology - the chronology, not null
      name - the name of the type, not null
      periodUnit - the period unit, may be null
      periodRange - the period range, may be null
  • Method Details

    • getChronology

      public final Chronology getChronology()
      Gets the chronology of the rule.
      Returns:
      the chronology of the rule, never null
    • getID

      public final String getID()
      Gets the ID of the rule.

      The ID is of the form 'ChronologyName.RuleName'. No two fields should have the same ID.

      Returns:
      the ID of the rule, never null
    • getName

      public final String getName()
      Gets the name of the rule.

      Implementations should use the name that best represents themselves. If the rule represents a field, then the form 'UnitOfRange' should be used. Otherwise, use the simple class name of the generic type, such as 'ZoneOffset'.

      Returns:
      the name of the rule, never null
    • getPeriodUnit

      public PeriodUnit getPeriodUnit()
      Gets the unit that the rule is measured in.

      Most rules define a field such as 'hour of day' or 'month of year'. The unit is the period that varies within the range.

      For example, the rule for hour-of-day will return Hours, while the rule for month-of-year will return Months. The rule for a date will return Days as a date could alternately be described as 'days of forever'.

      The null value is returned if the rule is not defined by a unit and range.

      Returns:
      the unit defining the rule unit, null if this rule isn't based on a period
    • getPeriodRange

      public PeriodUnit getPeriodRange()
      Gets the range that the rule is bound by.

      Most rules define a field such as 'hour of day' or 'month of year'. The range is the period that the field varies within.

      For example, the rule for hour-of-day will return Days, while the rule for month-of-year will return Years.

      When the range is unbounded, such as for a date or the year field, then null will be returned. The null value is also returned if the rule is not defined by a unit and range.

      Returns:
      the unit defining the rule range, null if unbounded, or if this rule isn't based on a period
    • getReifiedType

      public final Class<T> getReifiedType()
      Gets the reified class representing the underlying type of the rule.

      Each rule uses an underlying type to represent the data. This is captured in the generic type of the rule. Since the generic implementation is Java is limited to the compiler, the underlying type has been reified and made available through this method. It is expected, but not enforced, that the underlying type is Comparable.

      Returns:
      the reified type of values of the rule, never null
    • reify

      public final T reify(Object value)
      Returns the input value cast to the correct generic type.

      Each rule uses an underlying type to represent the data. This is captured in the generic type of the rule. Since the generic implementation is Java is limited to the compiler, the underlying type has been reified which allows this method to validate the generic type fully. The implementation simply returns the input value typed as the generic type.

      Parameters:
      value - the value to reify, may be null
      Returns:
      the type-cast input value, may be null
      Throws:
      ClassCastException - if the value is not of the reified type
    • getValue

      public final T getValue(Calendrical calendrical)
      Gets the value of this rule from the specified calendrical returning null if the value cannot be returned.

      This method simply queries the calendrical.

      Parameters:
      calendrical - the calendrical to get the field value from, not null
      Returns:
      the value of the field, null if unable to extract the field
    • getValueChecked

      public final T getValueChecked(Calendrical calendrical)
      Gets the value of the rule from the specified calendrical throwing an exception if the rule cannot be returned.

      This convenience method uses getValue(Calendrical) to find the value and then ensures it isn't null.

      Parameters:
      calendrical - the calendrical to get the field value from, not null
      Returns:
      the value of the field, never null
      Throws:
      UnsupportedRuleException - if the rule cannot be extracted
    • deriveValueFor

      public final <R> R deriveValueFor(CalendricalRule<R> rule, T value, Calendrical calendrical, Chronology chronology)
      Derives the value of the specified rule from a calendrical.

      This method is provided for implementations of Calendrical.get(javax.time.calendar.CalendricalRule<T>) and is rarely called directly by application code. It is used when the calendrical has its own rule, and this method is called on the rule of the calendrical implementation, not the rule passed into the get method.

         public  T get(CalendricalRule rule) {
           return IMPLEMENTATION_RULE.deriveValueFor(rule, this, this);
         }
       
      The last parameter in the code snippet above is always this, however the second parameter may be a different representation, for example in Year.get(javax.time.calendar.CalendricalRule<T>).

      If this rule and the specified rule are the same, then the value is returned. Otherwise, an attempt is made to derive(javax.time.calendar.Calendrical) the field value.

      Parameters:
      rule - the rule to retrieve, not null
      value - the value to return if this rule is the specified rule, not null
      calendrical - the calendrical to get the value from, not null
      chronology - the chronology the value belongs to, null if chronology neutral
      Returns:
      the value, null if unable to derive the value
    • deriveValueFrom

      public final T deriveValueFrom(Calendrical calendrical)
      Derives the value of this rule from a calendrical.

      This method is provided for implementations of Calendrical.get(javax.time.calendar.CalendricalRule<T>) and is rarely called directly by application code. It is used when the calendrical has its own values but does not have its own rule.

         public  T get(CalendricalRule rule) {
           // return data, for example
           if (rule.equals(...)) {
             return valueForRule;
           }
           // call this method
           return rule.deriveValueFrom(this);
         }
       
      Parameters:
      calendrical - the calendrical to get the value from, not null
      Returns:
      the value, null if unable to derive the value
    • derive

      protected T derive(Calendrical calendrical)
      Derives the value of this rule from a calendrical.

      This method derives the value for this field from other fields in the calendrical without directly querying the calendrical for the value.

      For example, if this field is quarter-of-year, then the value can be derived from month-of-year.

      The implementation only needs to derive the value based on its immediate parents. The use of Calendrical.get(javax.time.calendar.CalendricalRule<T>) will extract any further parents on demand.

      A typical implementation of this method obtains the parent value and performs a calculation. For example, here is a simple implementation for the quarter-of-year field:

       Integer moyVal = calendrical.get(ISOChronology.monthOfYearRule());
       return (moyVal != null ? ((moyVal - 1) % 4) + 1) : null;
       

      This method is designed to be overridden in subclasses. The subclass implementation must be thread-safe. The subclass implementation must not request the value of this rule from the specified calendrical, otherwise a stack overflow error will occur.

      Parameters:
      calendrical - the calendrical to derive from, not null
      Returns:
      the derived value, null if unable to derive
    • interpretValue

      final T interpretValue(CalendricalMerger merger, Object value)
      Interprets the specified value converting it into an in range value of the correct type for this rule.
      Parameters:
      merger - the merger instance controlling the merge process, not null
      value - the value to interpret, not null
    • interpret

      protected T interpret(CalendricalMerger merger, Object value)
      Interprets the specified value converting it into an in range value of the correct type for this rule.

      Before this method is called, the value will be checked to ensure it is not of the type of this rule.

      Parameters:
      merger - the merger instance controlling the merge process, not null
      value - the value to interpret, null if unable to interpret the value
      Returns:
      the interpreted value
    • merge

      protected void merge(CalendricalMerger merger)
      Merges this field with other fields to form higher level fields.

      The aim of this method is to assist in the process of extracting the most date-time information possible from a map of field-value pairs. The merging process is controlled by the mutable merger instance and the input and output of the this merge are held there.

      Subclasses that override this method may use methods on the merger to obtain the values to merge. The value is guaranteed to be available for this field if this method is called.

      If the override successfully merged some fields then the following must be performed. The merged field must be stored using CalendricalMerger.storeMerged(javax.time.calendar.CalendricalRule<T>, T). Each field used in the merge must be marked as being used by calling CalendricalMerger.removeProcessed(javax.time.calendar.CalendricalRule<?>).

      An example to merge two fields into one - hour of AM/PM and AM/PM:

        Integer hapVal = merger.getValue(ISOChronology.hourOfAmPmRule());
        if (hapVal != null) {
          AmPmOfDay amPm = merger.getValue(this);
          int hourOfDay = MathUtils.safeAdd(MathUtils.safeMultiply(amPm, 12), hapVal);
          merger.storeMerged(ISOChronology.hourOfDayRule(), hourOfDay);
          merger.removeProcessed(this);
          merger.removeProcessed(ISOChronology.hourOfAmPmRule());
        }
       
      Parameters:
      merger - the merger instance controlling the merge process, not null
    • compare

      public int compare(Calendrical cal1, Calendrical cal2)
      Compares two Calendrical implementations based on the value of this rule extracted from each calendrical.

      This implements the Comparator interface and allows any two Calendrical implementations to be compared using this rule. The comparison is based on the result of calling Calendrical.get(javax.time.calendar.CalendricalRule<T>) on each calendrical, and comparing those values.

      For example, to sort a list into year order when the list may contain any mixture of calendricals, such as a LocalDate, YearMonth and ZonedDateTime:

        List list = ...
        Collections.sort(list, ISOChronology.yearRule());
       
      If the value of this rule cannot be obtained from a calendrical, then an exception is thrown.

      If the underlying type of this rule does not implement Comparable then an exception will be thrown.

      Specified by:
      compare in interface Comparator<T>
      Parameters:
      cal1 - the first calendrical to compare, not null
      cal2 - the second calendrical to compare, not null
      Returns:
      the comparator result, negative if first is less, positive if first is greater, zero if equal
      Throws:
      NullPointerException - if either input is null
      ClassCastException - if this rule has a type that is not comparable
      IllegalArgumentException - if this rule cannot be extracted from either input parameter
    • compareTo

      public int compareTo(CalendricalRule<?> other)
      Compares this CalendricalRule to another.

      The comparison is based on the period unit followed by the period range followed by the rule ID. The period unit is compared first, so MinuteOfHour will be less than HourOfDay, which will be less than DayOfWeek. When the period unit is the same, the period range is compared, so DayOfWeek is less than DayOfMonth, which is less than DayOfYear. Finally, the rule ID is compared.

      Specified by:
      compareTo in interface Comparable<T>
      Parameters:
      other - the other type to compare to, not null
      Returns:
      the comparator result, negative if less, positive if greater, zero if equal
      Throws:
      NullPointerException - if other is null
    • equals

      public boolean equals(Object obj)
      Compares two rules based on their ID.
      Specified by:
      equals in interface Comparator<T>
      Overrides:
      equals in class Object
      Returns:
      true if the rules are the same
    • hashCode

      public int hashCode()
      Returns a hash code based on the ID.
      Overrides:
      hashCode in class Object
      Returns:
      a description of the rule
    • toString

      public String toString()
      Returns a string representation of the rule.
      Overrides:
      toString in class Object
      Returns:
      a description of the rule, never null