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.isNull;
024import static org.tquadrat.foundation.lang.Objects.nonNull;
025import static org.tquadrat.foundation.util.StringUtils.isEmptyOrBlank;
026
027import java.io.Serial;
028import java.net.MalformedURLException;
029import java.net.URI;
030import java.net.URISyntaxException;
031import java.net.URL;
032import java.util.Collection;
033import java.util.List;
034
035import org.apiguardian.api.API;
036import org.tquadrat.foundation.annotation.ClassVersion;
037import org.tquadrat.foundation.exception.ValidationException;
038import org.tquadrat.foundation.lang.StringConverter;
039
040/**
041 *  <p>{@summary An implementation of
042 *  {@link StringConverter}
043 *  for
044 *  {@link URL}
045 *  values.}</p>
046 *  <p>The method
047 *  {@link #fromString(CharSequence)}
048 *  will use the constructor
049 *  {@link URI#URI(String)}
050 *  and then
051 *  {@link URI#toURL()}
052 *  to create a {@code URL} instance from the given value. It requires that the
053 *  URL is absolute.</p>
054 *  <p>Generally, when dealing with relative URLs, URIs should be used
055 *  instead.</p>
056 *
057 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
058 *  @version $Id: URLStringConverter.java 1157 2025-12-31 14:05:44Z tquadrat $
059 *  @since 0.0.6
060 *
061 *  @UMLGraph.link
062 */
063@ClassVersion( sourceVersion = "$Id: URLStringConverter.java 1157 2025-12-31 14:05:44Z tquadrat $" )
064@API( status = STABLE, since = "0.0.6" )
065public sealed class URLStringConverter implements StringConverter<URL>
066    permits EncodedURLStringConverter
067{
068        /*-----------*\
069    ====** Constants **========================================================
070        \*-----------*/
071    /**
072     *  The error message for an invalid URL: {@value}.
073     */
074    public static final String MSG_InvalidURL = "'%1$s' cannot be parsed as a valid URL";
075
076        /*------------------------*\
077    ====** Static Initialisations **===========================================
078        \*------------------------*/
079    /**
080     *  The serial version UID for objects of this class: {@value}.
081     *
082     *  @hidden
083     */
084    @Serial
085    private static final long serialVersionUID = 1L;
086
087    /**
088     *  An instance of this class.
089     */
090    public static final URLStringConverter INSTANCE = new URLStringConverter();
091
092        /*--------------*\
093    ====** Constructors **=====================================================
094        \*--------------*/
095    /**
096     *  Creates a new instance of {@code URLStringConverter}.
097     */
098    public URLStringConverter() {}
099
100        /*---------*\
101    ====** Methods **==========================================================
102        \*---------*/
103    /**
104     *  {@inheritDoc}
105     */
106    @Override
107    public URL fromString( final CharSequence source ) throws IllegalArgumentException
108    {
109        URL retValue = null;
110        if( nonNull( source ) )
111        {
112            try
113            {
114                if( isEmptyOrBlank( source ) ) throw new ValidationException( "URL is blank or empty" );
115                final var uri = new URI( source.toString() );
116                retValue = uri.toURL();
117            }
118            catch( final MalformedURLException | URISyntaxException | ValidationException e )
119            {
120                throw new IllegalArgumentException( format( MSG_InvalidURL, source ), e );
121            }
122        }
123
124        //---* Done *----------------------------------------------------------
125        return retValue;
126    }   //  fromString()
127
128    /**
129     *  Provides the subject class for this converter.
130     *
131     *  @return The subject class.
132     */
133    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
134    public final Collection<Class<?>> getSubjectClass() { return List.of( URL.class ); }
135
136    /**
137     *  This method is used by the
138     *  {@link java.util.ServiceLoader}
139     *  to obtain the instance for this
140     *  {@link org.tquadrat.foundation.lang.StringConverter}
141     *  implementation.
142     *
143     *  @return The instance for this {@code StringConverter} implementation.
144     */
145    public static final URLStringConverter provider() { return INSTANCE; }
146
147    /**
148     *  {@inheritDoc}
149     */
150    @Override
151    public String toString( final URL source )
152    {
153        final var retValue = isNull( source ) ? null : source.toExternalForm();
154
155        //---* Done *----------------------------------------------------------
156        return retValue;
157    }   //  toString()
158}
159//  class URLStringConverter
160
161/*
162 *  End of File
163 */