001/* 002 * ============================================================================ 003 * Copyright © 2002-2024 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * 007 * Licensed to the public under the agreements of the GNU Lesser General Public 008 * License, version 3.0 (the "License"). You may obtain a copy of the License at 009 * 010 * http://www.gnu.org/licenses/lgpl.html 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 014 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 015 * License for the specific language governing permissions and limitations 016 * under the License. 017 */ 018 019package org.tquadrat.foundation.util.stringconverter; 020 021import static java.time.format.SignStyle.NORMAL; 022import static java.time.temporal.ChronoField.YEAR; 023import static org.apiguardian.api.API.Status.STABLE; 024import static org.tquadrat.foundation.lang.Objects.nonNull; 025 026import java.io.Serial; 027import java.time.Year; 028import java.time.format.DateTimeFormatter; 029import java.time.format.DateTimeFormatterBuilder; 030import java.time.format.DateTimeParseException; 031import java.util.Optional; 032 033import org.apiguardian.api.API; 034import org.tquadrat.foundation.annotation.ClassVersion; 035 036/** 037 * The implementation of 038 * {@link TimeDateStringConverter} 039 * for {@code java.time.Year}. 040 * 041 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 042 * @version $Id: YearStringConverter.java 1130 2024-05-05 16:16:09Z tquadrat $ 043 * @since 0.0.6 044 * 045 * @UMLGraph.link 046 */ 047@ClassVersion( sourceVersion = "$Id: YearStringConverter.java 1130 2024-05-05 16:16:09Z tquadrat $" ) 048@API( status = STABLE, since = "0.0.6" ) 049public class YearStringConverter extends TimeDateStringConverter<Year> 050{ 051 /*------------------------*\ 052 ====** Static Initialisations **=========================================== 053 \*------------------------*/ 054 /** 055 * The serial version UID for objects of this class: {@value}. 056 * 057 * @hidden 058 */ 059 @Serial 060 private static final long serialVersionUID = 1L; 061 062 /** 063 * An instance of this class. 064 */ 065 public static final YearStringConverter INSTANCE = new YearStringConverter(); 066 067 /*--------------*\ 068 ====** Constructors **===================================================== 069 \*--------------*/ 070 /** 071 * Creates a new {@code YearStringConverter} instance. 072 */ 073 public YearStringConverter() { super( Year.class ); } 074 075 /** 076 * Creates a new {@code YearStringConverter} instance. 077 * 078 * @param formatter The formatter for the temporal accessor. 079 */ 080 public YearStringConverter( final DateTimeFormatter formatter ) 081 { 082 super( Year.class, formatter ); 083 } // YearStringConverter() 084 085 /*---------*\ 086 ====** Methods **========================================================== 087 \*---------*/ 088 /** 089 * {@inheritDoc} 090 */ 091 @Override 092 protected final Year parseDateTime( final CharSequence source, final Optional<DateTimeFormatter> formatter ) throws DateTimeParseException 093 { 094 Year retValue = null; 095 if( nonNull( source ) ) 096 { 097 /* 098 * We use Year.toString() when converting a Year to a String; this 099 * results in the String "-333" for the year of the Battle at 100 * Knossos. 101 * Unfortunately, the method Year.parse(CharSequence) will call 102 * internally the method Year.parse(CharSequence,DateTimeFormatter) 103 * with the internally defined instance PARSER as its second 104 * argument. 105 * 106 * PARSER is defined as below: 107 * private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder() 108 * .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) 109 * .toFormatter(); 110 * 111 * This will cause a DateTimeParseException for all years with less 112 * than four digits. Consequently, we have to define our own 113 * DateTimeFormatter when none was provided to the StringConverter. 114 * 115 * When constructing the DateTimeFormatter, a call to 116 * DateTimeFormatterBuilder.parseLenient() is required if we keep 117 * the SignStyle EXCEEDS_PAD, because no sign is printed for years 118 * after 0. We decided to use NORMAL instead, staying with strict 119 * parsing. 120 */ 121 final var parser = formatter.orElseGet( () -> 122 new DateTimeFormatterBuilder() 123// .parseLenient() 124// .appendValue( YEAR, 1, 10, EXCEEDS_PAD ) 125 .appendValue( YEAR, 1, 10, NORMAL ) 126 .toFormatter() ); 127 retValue = Year.parse( source, parser ); 128 } 129 130 //---* Done *---------------------------------------------------------- 131 return retValue; 132 } // parseDateTime() 133 134 /** 135 * This method is used by the 136 * {@link java.util.ServiceLoader} 137 * to obtain the instance for this 138 * {@link org.tquadrat.foundation.lang.StringConverter} 139 * implementation. 140 * 141 * @return The instance for this {@code StringConverter} implementation. 142 */ 143 public static final YearStringConverter provider() { return INSTANCE; } 144} 145// class YearStringConverter 146 147/* 148 * End of File 149 */