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