001/* 002 * ============================================================================ 003 * Copyright © 2002-2023 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.lang.String.format; 022import static org.apiguardian.api.API.Status.STABLE; 023import static org.tquadrat.foundation.lang.Objects.nonNull; 024 025import java.io.Serial; 026import java.time.Period; 027import java.time.format.DateTimeParseException; 028 029import org.apiguardian.api.API; 030import org.tquadrat.foundation.annotation.ClassVersion; 031import org.tquadrat.foundation.lang.StringConverter; 032 033/** 034 * <p>{@summary An implementation of 035 * {@link StringConverter} 036 * for 037 * {@link Period} 038 * values.}</p> 039 * <p>While the method 040 * {@link StringConverter#toString(Object) toString(Period)} 041 * simply uses 042 * {@link Period#toString()}, 043 * the method 044 * {@link #fromString(CharSequence)} 045 * uses 046 * {@link Period#parse(CharSequence)} 047 * to create the {@code Period} instance for the given value. The formats 048 * accepted are based on the ISO-8601 period formats {@code PnYnMnD} and 049 * {@code PnW}.</p> 050 * <p>The string starts with an optional sign, denoted by the ASCII negative 051 * ('-') or positive ('+') symbol. If negative, the whole period is 052 * negated.</p> 053 * <p>The ASCII letter "P" is next in upper or lower case.</p> 054 * <p>There are then four sections, each consisting of a number and a suffix. 055 * At least one of the four sections must be present.</p> 056 * <p>The sections have suffixes in ASCII of "Y", "M", 057 * "W" and "D" for years, months, weeks and days, accepted 058 * in upper or lower case.</p> 059 * <p>The suffixes must occur in order. The number part of each section must 060 * consist of ASCII digits. The number may be prefixed by the ASCII negative 061 * or positive symbol. The number must parse to an {@code int}.</p> 062 * 063 * @note The leading plus/minus sign, and negative values for other units are 064 * not originally part of the ISO-8601 standard. 065 * @note In addition, ISO-8601 does not permit mixing between the 066 * {@code PnYnMnD} and {@code PnW} formats. Any week-based input is 067 * multiplied by 7 and treated as a number of days. 068 * 069 * @see Period 070 * 071 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 072 * @version $Id: PeriodStringConverter.java 1060 2023-09-24 19:21:40Z tquadrat $ 073 * @since 0.0.7 074 * 075 * @UMLGraph.link 076 */ 077@ClassVersion( sourceVersion = "$Id: PeriodStringConverter.java 1060 2023-09-24 19:21:40Z tquadrat $" ) 078@API( status = STABLE, since = "0.0.7" ) 079public final class PeriodStringConverter implements StringConverter<Period> 080{ 081 /*-----------*\ 082 ====** Constants **======================================================== 083 \*-----------*/ 084 /** 085 * The error message for an invalid period String: {@value}. 086 */ 087 public static final String MSG_InvalidPeriod = "'%1$s' cannot be parsed as a valid period"; 088 089 /*------------------------*\ 090 ====** Static Initialisations **=========================================== 091 \*------------------------*/ 092 /** 093 * The serial version UID for objects of this class: {@value}. 094 * 095 * @hidden 096 */ 097 @Serial 098 private static final long serialVersionUID = 1L; 099 100 /** 101 * An instance of this class. 102 */ 103 public static final PeriodStringConverter INSTANCE = new PeriodStringConverter(); 104 105 /*--------------*\ 106 ====** Constructors **===================================================== 107 \*--------------*/ 108 /** 109 * Creates a new instance of {@code PeriodStringConverter}. 110 */ 111 public PeriodStringConverter() {} 112 113 /*---------*\ 114 ====** Methods **========================================================== 115 \*---------*/ 116 /** 117 * {@inheritDoc} 118 */ 119 @Override 120 public final Period fromString( final CharSequence source ) throws IllegalArgumentException 121 { 122 Period retValue = null; 123 if( nonNull( source ) ) 124 { 125 try 126 { 127 retValue = Period.parse( source ); 128 } 129 catch( final DateTimeParseException e ) 130 { 131 throw new IllegalArgumentException( format( MSG_InvalidPeriod, source ), e ); 132 } 133 } 134 135 //---* Done *---------------------------------------------------------- 136 return retValue; 137 } // fromString() 138 139 /** 140 * This method is used by the 141 * {@link java.util.ServiceLoader} 142 * to obtain the instance for this 143 * {@link org.tquadrat.foundation.lang.StringConverter} 144 * implementation. 145 * 146 * @return The instance for this {@code StringConverter} implementation. 147 */ 148 public static final PeriodStringConverter provider() { return INSTANCE; } 149} 150// class PeriodStringConverter 151 152/* 153 * End of File 154 */