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.transforms; 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 */ 029public class DefaultingTemporalAccessor implements TemporalAccessor { 030 031 private final TemporalAccessor inner; 032 private final ZoneId zoneId; 033 private ZonedDateTime zdt; 034 035 /** 036 * Constructor. 037 * 038 * @param inner The temporal accessor being wrapped. 039 * @param zoneId The default zone ID if it's not specified in the accessor. 040 */ 041 public DefaultingTemporalAccessor(TemporalAccessor inner, ZoneId zoneId) { 042 this.inner = inner; 043 this.zoneId = zoneId; 044 } 045 046 @Override /* TemporalAccessor */ 047 public boolean isSupported(TemporalField field) { 048 return inner.isSupported(field); 049 } 050 051 @Override /* TemporalAccessor */ 052 @SuppressWarnings("unchecked") 053 public <R> R query(TemporalQuery<R> query) { 054 R r = inner.query(query); 055 056 if (r != null) 057 return r; 058 059 if (query == zone() || query == zoneId()) 060 return (R)zoneId; 061 062 if (query == localTime()) { 063 064 if (isSupported(INSTANT_SECONDS)) 065 return (R)zdt().toLocalTime(); 066 067 int hour = 0; 068 if (isSupported(HOUR_OF_DAY)) 069 hour = iget(HOUR_OF_DAY); 070 else if (isSupported(HOUR_OF_AMPM)) 071 hour = iget(HOUR_OF_AMPM) + 12 * iget(AMPM_OF_DAY); 072 073 int minute = isSupported(MINUTE_OF_HOUR) ? iget(MINUTE_OF_HOUR) : 0; 074 int second = isSupported(SECOND_OF_MINUTE) ? iget(SECOND_OF_MINUTE) : 0; 075 int nano = isSupported(NANO_OF_SECOND) ? iget(NANO_OF_SECOND) : 0; 076 077 return (R)LocalTime.of(hour, minute, second, nano); 078 } 079 080 if (query == localDate()) { 081 082 if (isSupported(INSTANT_SECONDS)) 083 return (R)zdt().toLocalDate(); 084 085 int year = isSupported(YEAR) ? iget(ChronoField.YEAR) : 1970; 086 int month = isSupported(MONTH_OF_YEAR) ? iget(MONTH_OF_YEAR) : 1; 087 int dayOfMonth = isSupported(DAY_OF_MONTH) ? iget(DAY_OF_MONTH) : 1; 088 089 return (R)LocalDate.of(year, Month.of(month), dayOfMonth); 090 } 091 092 if (query == offset()) { 093 return (R)zoneId.getRules().getOffset(zdt().toInstant()); 094 } 095 096 return null; 097 } 098 099 @Override /* TemporalAccessor */ 100 public long getLong(TemporalField field) { 101 102 if (isSupported(field)) 103 return inner.getLong(field); 104 105 if (field == INSTANT_SECONDS) 106 return zdt().toEpochSecond(); 107 108 if (field == EPOCH_DAY) 109 return zdt().toLocalDate().toEpochDay(); 110 111 if (field == YEAR) { 112 if (isSupported(INSTANT_SECONDS)) 113 return zdt().toLocalDate().getYear(); 114 return 1970; 115 } 116 117 if (field == MONTH_OF_YEAR) { 118 if (isSupported(INSTANT_SECONDS)) 119 return zdt().toLocalDate().getMonthValue(); 120 return 1; 121 } 122 123 return 0; 124 } 125 126 @Override /* TemporalAccessor */ 127 public int get(TemporalField field) { 128 if (inner.isSupported(field)) 129 return inner.get(field); 130 return (int)getLong(field); 131 } 132 133 private int iget(TemporalField field) { 134 return inner.get(field); 135 } 136 137 private ZonedDateTime zdt() { 138 if (zdt == null) 139 zdt = ZonedDateTime.from(this); 140 return zdt; 141 } 142}