001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.swaps;
014
015import static java.time.temporal.ChronoField.*;
016import static java.time.temporal.TemporalQueries.*;
017
018import java.time.*;
019import java.time.format.*;
020import java.time.temporal.*;
021
022/**
023 * Wraps a {@link TemporalAccessor} to provide default values wherever possible instead of throwing unsupported field exceptions.
024 *
025 * <p>
026 * If working correctly, any <c>TemporalAccessor</c> returned by the {@link DateTimeFormatter#parse(CharSequence)} method
027 * should be able to be passed to any <code>Temporal.from(TemporalAccessor)</code> static method (such as {@link ZonedDateTime#from(TemporalAccessor)}).
028 *
029 * <h5 class='section'>See Also:</h5><ul>
030 *    <li class='link'><a class="doclink" href="../../../../index.html#jm.Swaps">Swaps</a>
031 * </ul>
032 */
033public class DefaultingTemporalAccessor implements TemporalAccessor {
034
035   private final TemporalAccessor inner;
036   private final ZoneId zoneId;
037   private ZonedDateTime zdt;
038
039   /**
040    * Constructor.
041    *
042    * @param inner The temporal accessor being wrapped.
043    * @param zoneId The default zone ID if it's not specified in the accessor.
044    */
045   public DefaultingTemporalAccessor(TemporalAccessor inner, ZoneId zoneId) {
046      this.inner = inner;
047      this.zoneId = zoneId;
048   }
049
050   @Override /* TemporalAccessor */
051   public boolean isSupported(TemporalField field) {
052      return inner.isSupported(field);
053   }
054
055   @Override /* TemporalAccessor */
056   @SuppressWarnings("unchecked")
057   public <R> R query(TemporalQuery<R> query) {
058      R r = inner.query(query);
059
060      if (r != null)
061         return r;
062
063      if (query == zone() || query == zoneId())
064         return (R)zoneId;
065
066      if (query == localTime()) {
067
068         if (isSupported(INSTANT_SECONDS))
069            return (R)zdt().toLocalTime();
070
071         int hour = 0;
072         if (isSupported(HOUR_OF_DAY))
073            hour = iget(HOUR_OF_DAY);
074         else if (isSupported(HOUR_OF_AMPM))
075            hour = iget(HOUR_OF_AMPM) + 12 * iget(AMPM_OF_DAY);
076
077         int minute = isSupported(MINUTE_OF_HOUR) ? iget(MINUTE_OF_HOUR) : 0;
078         int second = isSupported(SECOND_OF_MINUTE) ? iget(SECOND_OF_MINUTE) : 0;
079         int nano = isSupported(NANO_OF_SECOND) ? iget(NANO_OF_SECOND) : 0;
080
081         return (R)LocalTime.of(hour, minute, second, nano);
082      }
083
084      if (query == localDate()) {
085
086         if (isSupported(INSTANT_SECONDS))
087            return (R)zdt().toLocalDate();
088
089         int year = isSupported(YEAR) ? iget(ChronoField.YEAR) : 1970;
090         int month = isSupported(MONTH_OF_YEAR) ? iget(MONTH_OF_YEAR) : 1;
091         int dayOfMonth = isSupported(DAY_OF_MONTH) ? iget(DAY_OF_MONTH) : 1;
092
093         return (R)LocalDate.of(year, Month.of(month), dayOfMonth);
094      }
095
096      if (query == offset()) {
097         return (R)zoneId.getRules().getOffset(zdt().toInstant());
098      }
099
100      return null;
101   }
102
103   @Override /* TemporalAccessor */
104   public long getLong(TemporalField field) {
105
106      if (isSupported(field))
107         return inner.getLong(field);
108
109      if (field == INSTANT_SECONDS)
110         return zdt().toEpochSecond();
111
112      if (field == EPOCH_DAY)
113         return zdt().toLocalDate().toEpochDay();
114
115      if (field == YEAR) {
116         if (isSupported(INSTANT_SECONDS))
117            return zdt().toLocalDate().getYear();
118         return 1970;
119      }
120
121      if (field == MONTH_OF_YEAR) {
122         if (isSupported(INSTANT_SECONDS))
123            return zdt().toLocalDate().getMonthValue();
124         return 1;
125      }
126
127      return 0;
128   }
129
130   @Override /* TemporalAccessor */
131   public int get(TemporalField field) {
132      if (inner.isSupported(field))
133         return inner.get(field);
134      return (int)getLong(field);
135   }
136
137   private int iget(TemporalField field) {
138      return inner.get(field);
139   }
140
141   private ZonedDateTime zdt() {
142      if (zdt == null)
143         zdt = ZonedDateTime.from(this);
144      return zdt;
145   }
146}