001/* 002 * ============================================================================ 003 * Copyright © 2002-2026 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 org.apiguardian.api.API; 022import org.tquadrat.foundation.annotation.ClassVersion; 023import org.tquadrat.foundation.lang.StringConverter; 024import org.tquadrat.foundation.lang.value.Dimension; 025import org.tquadrat.foundation.lang.value.DimensionedValue; 026 027import java.io.Serial; 028import java.math.BigDecimal; 029import java.util.Collection; 030import java.util.List; 031 032import static java.lang.String.format; 033import static java.util.Locale.ROOT; 034import static org.apiguardian.api.API.Status.STABLE; 035import static org.tquadrat.foundation.lang.Objects.isNull; 036import static org.tquadrat.foundation.lang.Objects.nonNull; 037import static org.tquadrat.foundation.util.StringUtils.isEmptyOrBlank; 038 039/** 040 * <p>{@summary The abstract base class for implementations of 041 * {@link StringConverter} 042 * for dimensioned values.}</p> 043 * <p>The String representations for all dimensioned values have the same 044 * format: a numeric part followed by the unit for the dimension, separated by 045 * whitespace (one or more blanks). For example: <code>15 m</code> or 046 * <code>16.0 t</code>.</p> 047 * 048 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 049 * @version $Id: DimensionedValueStringConverter.java 1195 2026-04-15 21:33:40Z tquadrat $ 050 * @since 0.25.3 051 * 052 * @param <D> The type for the dimension. 053 * @param <V> The type for the dimensioned value. 054 * 055 * @UMLGraph.link 056 */ 057@ClassVersion( sourceVersion = "$Id: DimensionedValueStringConverter.java 1195 2026-04-15 21:33:40Z tquadrat $" ) 058@API( status = STABLE, since = "0.25.3" ) 059public abstract class DimensionedValueStringConverter<D extends Dimension,V extends DimensionedValue<D>> implements StringConverter<V> 060{ 061 /*-----------*\ 062 ====** Constants **======================================================== 063 \*-----------*/ 064 /** 065 * The error message for an invalid value {@value}. 066 */ 067 public static final String MSG_InvalidValue = "'%s' cannot be parsed as a dimensioned value"; 068 069 /*------------*\ 070 ====** Attributes **======================================================= 071 \*------------*/ 072 /** 073 * The subject class for this converter. 074 * 075 * @serial 076 */ 077 private final Class<V> m_SubjectClass; 078 079 /*------------------------*\ 080 ====** Static Initialisations **=========================================== 081 \*------------------------*/ 082 /** 083 * The serial version UID for objects of this class: {@value}. 084 * 085 * @hidden 086 */ 087 @Serial 088 private static final long serialVersionUID = 1L; 089 090 /*--------------*\ 091 ====** Constructors **===================================================== 092 \*--------------*/ 093 /** 094 * Creates a new instance of {@code DimensionedValueStringConverter}. 095 * 096 * @param subjectClass The subject class. 097 */ 098 protected DimensionedValueStringConverter( final Class<V> subjectClass ) 099 { 100 m_SubjectClass = subjectClass; 101 } // DimensionedValueStringConverter() 102 103 /*---------*\ 104 ====** Methods **========================================================== 105 \*---------*/ 106 /** 107 * Creates an instance of 108 * {@link DimensionedValue} 109 * from the given arguments. 110 * 111 * @param number The value. 112 * @param dimension The dimension. 113 * @return The dimensioned value. 114 */ 115 protected abstract V createValue( BigDecimal number, D dimension ); 116 117 /** 118 * {@inheritDoc} 119 */ 120 @Override 121 public final V fromString( final CharSequence source ) throws IllegalArgumentException 122 { 123 V retValue = null; 124 if( nonNull( source ) ) 125 { 126 if( isEmptyOrBlank( source ) ) throw new IllegalArgumentException( format( MSG_InvalidValue, source ) ); 127 final var parts = source.toString().split( "\\s" ); 128 if( parts.length != 2 ) throw new IllegalArgumentException( format( MSG_InvalidValue, source ) ); 129 130 //---* Get the number *-------------------------------------------- 131 final var number = BigDecimalStringConverter.INSTANCE.fromString( parts [0] ); 132 133 //---* Get the dimension *----------------------------------------- 134 final var dimension = unitFromSymbol( parts [1] ); 135 136 //---* Create the return value *----------------------------------- 137 retValue = createValue( number, dimension ); 138 } 139 140 //---* Done *---------------------------------------------------------- 141 return retValue; 142 } // fromString() 143 144 /** 145 * Provides the subject class for this converter. 146 * 147 * @return The subject class. 148 */ 149 @SuppressWarnings( "PublicMethodNotExposedInInterface" ) 150 public final Collection<Class<V>> getSubjectClass() { return List.of( m_SubjectClass ); } 151 152 /** 153 * {@inheritDoc} 154 */ 155 @Override 156 public String toString( final V source ) 157 { 158 final var retValue = isNull( source ) ? null : source.toString( ROOT,-1, -1 ); 159 160 //---* Done *---------------------------------------------------------- 161 return retValue; 162 } // toString() 163 164 /** 165 * Returns a String representation of given value.<br> 166 * <br>The precision is applied to the numerical part only. The width 167 * includes the 168 * {@linkplain Dimension#unitSymbol() unit symbol}, too. 169 * 170 * @param source The object to convert; can be {@code null}. 171 * @param width The minimum number of characters to be written to the 172 * output. If the length of the converted value is less than the width 173 * then the output will be padded by ' ' until the total number 174 * of characters equals width. The padding is at the beginning, as 175 * numerical values are usually right justified. If {@code width} is 176 * -1 then there is no minimum. 177 * @param precision – The number of digits for the mantissa of the value. 178 * If {@code precision} is -1 then there is no explicit limit on the 179 * size of the mantissa. 180 * @return The String representation for this value. 181 */ 182 @SuppressWarnings( "PublicMethodNotExposedInInterface" ) 183 public String toString( final V source, final int width, final int precision ) 184 { 185 final var retValue = isNull( source ) ? null : source.toString( ROOT, width, precision ); 186 187 //---* Done *---------------------------------------------------------- 188 return retValue; 189 } // toString() 190 191 /** 192 * Determines the unit instance from the given unit symbol. 193 * 194 * @param symbol The unit symbol. 195 * @return The unit instance. 196 * @throws IllegalArgumentException The given unit symbol is unknown 197 * for the respective dimension. 198 */ 199 protected abstract D unitFromSymbol( String symbol ) throws IllegalArgumentException; 200} 201// class DimensionedValueStringConverter 202 203/* 204 * End of File 205 */