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}