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.*; 021 022import java.time.*; 023import java.time.format.*; 024import java.time.temporal.*; 025 026/** 027 * Wraps a {@link TemporalAccessor} to provide default values wherever possible instead of throwing unsupported field exceptions. 028 * 029 * <p> 030 * If working correctly, any <c>TemporalAccessor</c> returned by the {@link DateTimeFormatter#parse(CharSequence)} method 031 * should be able to be passed to any <code>Temporal.from(TemporalAccessor)</code> static method (such as {@link ZonedDateTime#from(TemporalAccessor)}). 032 * 033 * <h5 class='section'>See Also:</h5><ul> 034 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SwapBasics">Swap Basics</a> 035 * </ul> 036 */ 037public class DefaultingTemporalAccessor implements TemporalAccessor { 038 039 private final TemporalAccessor inner; 040 private final ZoneId zoneId; 041 private ZonedDateTime zdt; 042 043 /** 044 * Constructor. 045 * 046 * @param inner The temporal accessor being wrapped. 047 * @param zoneId The default zone ID if it's not specified in the accessor. 048 */ 049 public DefaultingTemporalAccessor(TemporalAccessor inner, ZoneId zoneId) { 050 this.inner = inner; 051 this.zoneId = zoneId; 052 } 053 054 @Override /* TemporalAccessor */ 055 public boolean isSupported(TemporalField field) { 056 return inner.isSupported(field); 057 } 058 059 @Override /* TemporalAccessor */ 060 @SuppressWarnings("unchecked") 061 public <R> R query(TemporalQuery<R> query) { 062 R r = inner.query(query); 063 064 if (r != null) 065 return r; 066 067 if (query == zone() || query == zoneId()) 068 return (R)zoneId; 069 070 if (query == localTime()) { 071 072 if (isSupported(INSTANT_SECONDS)) 073 return (R)zdt().toLocalTime(); 074 075 int hour = 0; 076 if (isSupported(HOUR_OF_DAY)) 077 hour = iget(HOUR_OF_DAY); 078 else if (isSupported(HOUR_OF_AMPM)) 079 hour = iget(HOUR_OF_AMPM) + 12 * iget(AMPM_OF_DAY); 080 081 int minute = isSupported(MINUTE_OF_HOUR) ? iget(MINUTE_OF_HOUR) : 0; 082 int second = isSupported(SECOND_OF_MINUTE) ? iget(SECOND_OF_MINUTE) : 0; 083 int nano = isSupported(NANO_OF_SECOND) ? iget(NANO_OF_SECOND) : 0; 084 085 return (R)LocalTime.of(hour, minute, second, nano); 086 } 087 088 if (query == localDate()) { 089 090 if (isSupported(INSTANT_SECONDS)) 091 return (R)zdt().toLocalDate(); 092 093 int year = isSupported(YEAR) ? iget(ChronoField.YEAR) : 1970; 094 int month = isSupported(MONTH_OF_YEAR) ? iget(MONTH_OF_YEAR) : 1; 095 int dayOfMonth = isSupported(DAY_OF_MONTH) ? iget(DAY_OF_MONTH) : 1; 096 097 return (R)LocalDate.of(year, Month.of(month), dayOfMonth); 098 } 099 100 if (query == offset()) { 101 return (R)zoneId.getRules().getOffset(zdt().toInstant()); 102 } 103 104 return null; 105 } 106 107 @Override /* TemporalAccessor */ 108 public long getLong(TemporalField field) { 109 110 if (isSupported(field)) 111 return inner.getLong(field); 112 113 if (field == INSTANT_SECONDS) 114 return zdt().toEpochSecond(); 115 116 if (field == EPOCH_DAY) 117 return zdt().toLocalDate().toEpochDay(); 118 119 if (field == YEAR) { 120 if (isSupported(INSTANT_SECONDS)) 121 return zdt().toLocalDate().getYear(); 122 return 1970; 123 } 124 125 if (field == MONTH_OF_YEAR) { 126 if (isSupported(INSTANT_SECONDS)) 127 return zdt().toLocalDate().getMonthValue(); 128 return 1; 129 } 130 131 return 0; 132 } 133 134 @Override /* TemporalAccessor */ 135 public int get(TemporalField field) { 136 if (inner.isSupported(field)) 137 return inner.get(field); 138 return (int)getLong(field); 139 } 140 141 private int iget(TemporalField field) { 142 return inner.get(field); 143 } 144 145 private ZonedDateTime zdt() { 146 if (zdt == null) 147 zdt = ZonedDateTime.from(this); 148 return zdt; 149 } 150}