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.lang; 020 021import static java.lang.String.format; 022import static java.util.function.Function.identity; 023import static org.apiguardian.api.API.Status.STABLE; 024import static org.tquadrat.foundation.lang.CommonConstants.NULL_STRING; 025import static org.tquadrat.foundation.lang.Objects.isNull; 026import static org.tquadrat.foundation.lang.Objects.nonNull; 027import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 028 029import java.io.File; 030import java.net.URL; 031import java.util.Formattable; 032import java.util.Optional; 033import java.util.function.Function; 034 035import org.apiguardian.api.API; 036import org.tquadrat.foundation.annotation.ClassVersion; 037 038/** 039 * <p>{@summary This interface defines a method to compose a String 040 * representation from arbitrary objects.}</p> 041 * <p>It will also provide several convenience implementations for the 042 * interface that can be used with special argument types. Currently, these 043 * are</p> 044 * <ul> 045 * <li>{@link #OBJECT_STRINGER} 046 * as a catch-all</li> 047 * <li>{@link #BASE_STRINGER} 048 * when the output of the default implementation of 049 * {@link Object#toString()} 050 * is desired even when a more sophisticated implementation of 051 * {@code toString()} is available</li> 052 * <li>{@link #OBJECTCLASS_STRINGER} 053 * when only the object's class should be returned instead of its 054 * contents/value.</li> 055 * <li>{@link #CLASS_STRINGER} 056 * for instances of 057 * {@link Class}</li> 058 * <li>{@link #FILEOBJECT_STRINGER} 059 * for instances of 060 * {@link File}</li> 061 * <li>{@link #OPTIONAL_STRINGER} 062 * for instances of 063 * {@link Optional}</li> 064 * <li>{@link #STRING_STRINGER} 065 * for instances of 066 * {@link String}</li> 067 * <li>{@link #URL_STRINGER} 068 * for instances of 069 * {@link URL} 070 * (There is no {@code URI_STRINGER} as 071 * {@link java.net.URI URI} 072 * provides a sufficient implementation of 073 * {@link Object#toString()})</li> 074 * </ul> 075 * <p>There are no explicit stringers for the classes from the {@code java.time} 076 * package as instances of these classes have sufficient implementations of 077 * {@link Object#toString()} 078 * and are therefore already well covered by 079 * {@link #OBJECT_STRINGER}. Same is true for the class 080 * {@link java.nio.file.Path}.</p> 081 * <p>This is a functional interface whose functional method is 082 * {@link #toString(Object)}.</p> 083 * 084 * @note The method {@code toString(Object)} will <i>never</i> return 085 * {@code null}; if called with a {@code null} argument, it will return 086 * the String "null".<br> 087 * This is different to the behaviour of the {@code toString(Object)} 088 * method of an implementation of {@code StringConverter} that returns 089 * {@code null} for a {@code null} argument. 090 * 091 * @see org.tquadrat.foundation.lang.StringConverter#toString(Object) 092 * @see CommonConstants#NULL_STRING 093 * 094 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 095 * @version $Id: Stringer.java 1060 2023-09-24 19:21:40Z tquadrat $ 096 * @since 0.1.0 097 * 098 * @UMLGraph.link 099 * 100 * @param <T> The type of the argument value. 101 */ 102@ClassVersion( sourceVersion = "$Id: Stringer.java 1060 2023-09-24 19:21:40Z tquadrat $" ) 103@FunctionalInterface 104@API( status = STABLE, since = "0.1.0" ) 105public interface Stringer<T> 106{ 107 /*-----------*\ 108 ====** Constants **======================================================== 109 \*-----------*/ 110 /** 111 * <p>{@summary An implementation of 112 * {@link Stringer} 113 * that produces an output that looks like that from the default 114 * implementation of 115 * {@link Object#toString()}}: 116 * it returns</p> 117 * <pre><code> o.getClass().getName() + "@" + Integer.toHexString( o.hashCode() );</code></pre>. 118 */ 119 @API( status = STABLE, since = "0.0.7" ) 120 public static final Stringer<? super Object> BASE_STRINGER = o -> isNull( o ) ? NULL_STRING : "%s@%x".formatted( o.getClass().getName(), o.hashCode() ); 121 122 /** 123 * <p>{@summary An implementation of 124 * {@link Stringer} 125 * for {@code byte} arrays.}</p> 126 * <p>The result looks like this:</p> 127 * <pre><code>byte [<<i>length</i>>]</code></pre> 128 * <p>where <code><i>length</i></code> is the array length of the given 129 * argument.</p> 130 */ 131 @API( status = STABLE, since = "0.0.5" ) 132 public static final Stringer<byte []> BYTEARRAY_STRINGER = a -> isNull( a ) ? NULL_STRING : "byte [%d]".formatted( a.length ); 133 134 /** 135 * An implementation of 136 * {@link Stringer} 137 * for instances of 138 * {@link Class}. 139 */ 140 @API( status = STABLE, since = "0.0.5" ) 141 public static final Stringer<Class<?>> CLASS_STRINGER = e -> isNull( e ) ? NULL_STRING : e.getName(); 142 143 /** 144 * A generic 145 * {@link Stringer} 146 * implementation that delegates to 147 * {@link Objects#toString(Object)}. 148 */ 149 @API( status = STABLE, since = "0.0.5" ) 150 public static final Stringer<Object> DEFAULT_STRINGER = o -> isNull( o ) ? NULL_STRING : o.toString(); 151 152 /** 153 * An implementation of 154 * {@link Stringer} 155 * for instances of 156 * {@link File}. 157 */ 158 @API( status = STABLE, since = "0.0.5" ) 159 public static final Stringer<File> FILEOBJECT_STRINGER = file -> isNull( file ) ? NULL_STRING : file.getAbsolutePath(); 160 161 /** 162 * A 163 * {@link Stringer} 164 * implementation that calls 165 * {@link String#format(String, Object...) String.format( "%s", o )} 166 * for an object {@code o} if that implements the interface 167 * {@link Formattable}, 168 * otherwise it delegates to 169 * {@link Objects#toString(Object)}. 170 * 171 * @see #DEFAULT_STRINGER 172 */ 173 @API( status = STABLE, since = "0.0.5" ) 174 public static final Stringer<? super Object> OBJECT_STRINGER = o -> o instanceof Formattable ? format( "%s", o ) : Objects.toString( o ); 175 176 /** 177 * An implementation of 178 * {@link Stringer} 179 * that returns the name of the object's class instead of a representation 180 * of the object's value. 181 */ 182 @API( status = STABLE, since = "0.0.5" ) 183 public static final Stringer<? super Object> OBJECTCLASS_STRINGER = o -> isNull( o ) ? NULL_STRING : o.getClass().getName(); 184 185 /** 186 * <p>{@summary An implementation of 187 * {@link Stringer} 188 * for instances of 189 * {@link Optional}.}</p> 190 * <p>It returns basically the result of 191 * {@link Objects#toString(Object,String)} 192 * with the value of the {@code Optional} and the text 193 * "{@code [empty]}" as the {@code nullDefault} arguments.</p> 194 */ 195 @API( status = STABLE, since = "0.0.5" ) 196 public static final Stringer<Optional<?>> OPTIONAL_STRINGER = o -> isNull( o ) ? NULL_STRING : "Optional: %s".formatted( o.map( v -> "%s = %s".formatted( v.getClass().getName(), OBJECT_STRINGER.toString( v ) ) ).orElse( "[empty]" ) ); 197 198 /** 199 * An implementation for 200 * {@link Stringer} 201 * for instances of 202 * {@link String}. 203 */ 204 @API( status = STABLE, since = "0.0.5" ) 205 public static final Stringer<String> STRING_STRINGER = wrapFunction( identity() ); 206 207 /** 208 * An implementation for 209 * {@link Stringer} 210 * for instances of 211 * {@link URL}. 212 */ 213 @API( status = STABLE, since = "0.0.5" ) 214 public static final Stringer<URL> URL_STRINGER = u -> isNull( u ) ? NULL_STRING : u.toExternalForm(); 215 216 /*---------*\ 217 ====** Methods **========================================================== 218 \*---------*/ 219 /** 220 * Returns the given {@code Stringer} as an instance of 221 * {@link Function}. 222 * 223 * @param <A> The type of the input for the stringer. 224 * @param stringer The instance to wrap. 225 * @return The function. 226 */ 227 @SuppressWarnings( "StaticMethodOnlyUsedInOneClass" ) 228 @API( status = STABLE, since = "0.0.5" ) 229 public static <A> Function<A,String> asFunction( final Stringer<? super A> stringer ) 230 { 231 final var retValue = (Function<A, String>) requireNonNullArgument( stringer, "stringer" )::toString; 232 233 //---* Done *---------------------------------------------------------- 234 return retValue; 235 } // asFunction() 236 237 /** 238 * <p>{@summary Creates a {@code Stringer} for the given instance of 239 * {@link StringConverter}.}</p> 240 * <p>A String converter cannot be used directly as a {@code Stringer} 241 * because {@code Stringer}'s 242 * {@link #toString(Object) toString()} 243 * method will never return {@code null} (in case the argument is 244 * {@code null}, it returns the 245 * {@link CommonConstants#NULL_STRING NULL_STRING}), 246 * while 247 * {@link StringConverter#toString(Object) StringConverter.toString()} 248 * returns {@code null} for a {@code null} argument. 249 * 250 * @param <A> The type of the input for the stringer. 251 * @param stringConverter The {@code StringConverter} instance. 252 * @return The {@code Stringer}. 253 */ 254 @API( status = STABLE, since = "0.0.7" ) 255 public static <A> Stringer<A> fromStringConverter( final StringConverter<? super A> stringConverter ) 256 { 257 final var sc = requireNonNullArgument( stringConverter, "stringConverter" ); 258 final var retValue = (Stringer<A>) a -> nonNull( a ) ? sc.toString( a ) : NULL_STRING; 259 260 //---* Done *---------------------------------------------------------- 261 return retValue; 262 } // fromStringConverter() 263 264 /** 265 * Returns this {@code Stringer} instance as an instance of 266 * {@link Function}. 267 * 268 * @return The function. 269 */ 270 @API( status = STABLE, since = "0.0.7" ) 271 public default Function<T,String> toFunction() 272 { 273 final var retValue = (Function<T, String>) this::toString; 274 275 //---* Done *---------------------------------------------------------- 276 return retValue; 277 } // toFunction() 278 279 /** 280 * Returns a String representation of the given argument, or 281 * {@link CommonConstants#NULL_STRING "null"} 282 * if the given {@code value} is {@code null}. 283 * 284 * @param value The value. 285 * @return The String representation. 286 */ 287 public String toString( final T value ); 288 289 /** 290 * Wraps a function that returns a String to an argument stringer. 291 * 292 * @param <A> The type of the input for the function. 293 * @param function The function to wrap. 294 * @return The argument stringer that wraps the function. 295 */ 296 @API( status = STABLE, since = "0.0.7" ) 297 public static <A> Stringer<A> wrapFunction( final Function<? super A, String> function ) 298 { 299 final var retValue = (Stringer<A>) requireNonNullArgument( function, "function" )::apply; 300 301 //---* Done *---------------------------------------------------------- 302 return retValue; 303 } // wrapFunction() 304} 305// interface Stringer 306 307/* 308 * End of File 309 */