001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 *     http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.gbif.common.parsers.date;
015
016import java.time.temporal.ChronoField;
017import java.time.temporal.TemporalAccessor;
018import java.util.Objects;
019
020import org.apache.commons.lang3.builder.HashCodeBuilder;
021import org.apache.commons.lang3.builder.ToStringBuilder;
022import org.apache.commons.lang3.builder.ToStringStyle;
023
024/**
025 * AtomizedLocalDate is a simple immutable class to hold local date data from a
026 * {@link TemporalAccessor}.
027 *
028 * Thread-Safe, immutable class.
029 */
030public class AtomizedLocalDate {
031
032  // Maximum resolution for a complete Local Date
033  public static final int COMPLETE_LOCAL_DATE_RESOLUTION = 3;
034
035  private final Integer year;
036  private final Integer month;
037  private final Integer day;
038
039  private final int resolution;
040
041  private AtomizedLocalDate(Integer year, Integer month, Integer day) {
042    this.year = year;
043    this.month = month;
044    this.day = day;
045
046    int res = 0;
047    if (year != null) {
048      res++;
049    }
050    if (month != null) {
051      res++;
052    }
053    if (day != null) {
054      res++;
055    }
056    resolution = res;
057  }
058
059  /**
060   * Build a new instance of {@link AtomizedLocalDate} based on a {@link TemporalAccessor}.
061   * This is done by extracting the {@link ChronoField}.
062   * @param temporalAccessor
063   * @return
064   */
065  public static AtomizedLocalDate fromTemporalAccessor(TemporalAccessor temporalAccessor) {
066    if (temporalAccessor == null) {
067      return null;
068    }
069
070    Integer y = null, m = null, d = null;
071    if (temporalAccessor.isSupported(ChronoField.YEAR)) {
072      y = temporalAccessor.get(ChronoField.YEAR);
073    }
074
075    if (temporalAccessor.isSupported(ChronoField.MONTH_OF_YEAR)) {
076      m = temporalAccessor.get(ChronoField.MONTH_OF_YEAR);
077    }
078
079    if (temporalAccessor.isSupported(ChronoField.DAY_OF_MONTH)) {
080      d = temporalAccessor.get(ChronoField.DAY_OF_MONTH);
081    }
082
083    return new AtomizedLocalDate(y, m, d);
084  }
085
086  public Integer getYear() {
087    return year;
088  }
089
090  public Integer getMonth() {
091    return month;
092  }
093
094  public Integer getDay() {
095    return day;
096  }
097
098  public int getResolution() {
099    return resolution;
100  }
101
102  /**
103   * Is the resolution of this local date matches the maximum.
104   * @return
105   */
106  public boolean isComplete() {
107    return COMPLETE_LOCAL_DATE_RESOLUTION == resolution;
108  }
109
110  @Override
111  public int hashCode() {
112    return new HashCodeBuilder().append(year).append(month).append(day).append(resolution).toHashCode();
113  }
114
115  @Override
116  public boolean equals(Object o) {
117    if (this == o) {
118      return true;
119    }
120    if (!(o instanceof AtomizedLocalDate)) {
121      return false;
122    }
123
124    AtomizedLocalDate that = (AtomizedLocalDate) o;
125
126    return resolution == that.resolution
127        && Objects.equals(year, that.year)
128        && Objects.equals(month, that.month)
129        && Objects.equals(day, that.day);
130  }
131
132  @Override
133  public String toString() {
134    return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
135            .append("year", year)
136            .append("month", month)
137            .append("day", day)
138            .append("resolution", resolution)
139            .toString();
140  }
141}