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