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 &quot;null&quot;.<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 [&lt;<i>length</i>&gt;]</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     *  &quot;{@code [empty]}&quot; 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 */