001/*
002 * ============================================================================
003 *  Copyright © 2002-2023 by Thomas Thrien.
004 *  All Rights Reserved.
005 * ============================================================================
006 *  Licensed to the public under the agreements of the GNU Lesser General Public
007 *  License, version 3.0 (the "License"). You may obtain a copy of the License at
008 *
009 *       http://www.gnu.org/licenses/lgpl.html
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
013 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014 *  License for the specific language governing permissions and limitations
015 *  under the License.
016 */
017
018package org.tquadrat.foundation.util.stringconverter;
019
020import static org.apiguardian.api.API.Status.STABLE;
021import static org.tquadrat.foundation.lang.CommonConstants.NULL_STRING;
022import static org.tquadrat.foundation.lang.Objects.isNull;
023import static org.tquadrat.foundation.lang.Objects.nonNull;
024import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument;
025
026import java.util.List;
027import java.util.StringJoiner;
028import java.util.function.Supplier;
029import java.util.regex.Pattern;
030import java.util.regex.PatternSyntaxException;
031
032import org.apiguardian.api.API;
033import org.tquadrat.foundation.annotation.ClassVersion;
034import org.tquadrat.foundation.lang.StringConverter;
035
036/**
037 *  <p>{@summary An implementation of
038 *  {@link org.tquadrat.foundation.lang.StringConverter}
039 *  for arbitrary instances of
040 *  {@link List }
041 *  implementations.}</p>
042 *  <p>The output of
043 *  {@link #toString(List)}
044 *  is quite different from that of a
045 *  {@link Object#toString() toString()}
046 *  method from one of the {@code List} implementations, and not really meant
047 *  for human readers.</p>
048 *
049 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
050 *  @version $Id: ListStringConverter.java 1080 2024-01-03 11:05:21Z tquadrat $
051 *  @since 0.3.0
052 *
053 *  @UMLGraph.link
054 *
055 *  @param  <E> The element type of the list to convert to a String.
056 */
057@ClassVersion( sourceVersion = "$Id: ListStringConverter.java 1080 2024-01-03 11:05:21Z tquadrat $" )
058@API( status = STABLE, since = "0.3.0" )
059public class ListStringConverter<E> implements StringConverter<List<E>>
060{
061        /*-----------*\
062    ====** Constants **========================================================
063        \*-----------*/
064    /**
065     *  The pattern that is used to identify an entry: {@value}.
066     */
067    public static final String PATTERN = "\\{\\{(.*?)\\}\\}";
068
069        /*------------*\
070    ====** Attributes **=======================================================
071        \*------------*/
072    /**
073     *  The instance of
074     *  {@link StringConverter}
075     *  that is used for the elements of the list.
076     *
077     *  @serial
078     */
079    private final StringConverter<E> m_ElementStringConverter;
080
081    /**
082     *  The factory for the list that is used by
083     *  {@link #fromString(CharSequence)}.
084     *
085     *  @serial
086     */
087    private final Supplier<List<E>> m_Factory;
088
089        /*------------------------*\
090    ====** Static Initialisations **===========================================
091        \*------------------------*/
092    /**
093     *  The pattern that is used to identify an entry.
094     */
095    private static final Pattern m_Pattern;
096
097    static
098    {
099        try
100        {
101            m_Pattern = Pattern.compile( PATTERN );
102        }
103        catch( final PatternSyntaxException e )
104        {
105            throw new ExceptionInInitializerError( e );
106        }
107    }
108
109        /*--------------*\
110    ====** Constructors **=====================================================
111        \*--------------*/
112    /**
113     *  Creates a new instance of {@code ListStringConverter}.
114     *
115     *  @param  converter   The instance of
116     *      {@link StringConverter}
117     *      that is used for the elements of the list.
118     *  @param  factory The factory for the list that is used by
119     *      {@link #fromString(CharSequence)}.
120     */
121    public ListStringConverter( final StringConverter<E> converter, final Supplier<List<E>> factory )
122    {
123        m_ElementStringConverter = requireNonNullArgument( converter, "converter" );
124        m_Factory = requireNonNullArgument( factory, "factory" );
125    }   //  ListStringConverter()
126
127        /*---------*\
128    ====** Methods **==========================================================
129        \*---------*/
130    /**
131     *  {@inheritDoc}
132     */
133    @Override
134    public final List<E> fromString( final CharSequence source ) throws IllegalArgumentException
135    {
136        List<E> retValue = null;
137        if( nonNull( source ) )
138        {
139            retValue = m_Factory.get();
140            if( !source.isEmpty() )
141            {
142                if( source.toString().startsWith( "[" ) && source.toString().endsWith( "]" ) )
143                {
144                    final var matcher = m_Pattern.matcher( source );
145                    while( matcher.find() )
146                    {
147                        retValue.add( m_ElementStringConverter.fromString( matcher.group(1) ) );
148                    }
149                }
150                else
151                {
152                    retValue.add( m_ElementStringConverter.fromString( source ) );
153                }
154            }
155        }
156
157        //---* Done *----------------------------------------------------------
158        return retValue;
159    }   //  fromString()
160
161    /**
162     *  {@inheritDoc}
163     */
164    @Override
165    public final String toString( final List<E> source )
166    {
167        String retValue = null;
168        if( nonNull( source ) )
169        {
170            final var buffer = new StringJoiner( "}},{{", "[{{", "}}]" );
171            buffer.setEmptyValue( "[]" );
172            for( final var element : source )
173            {
174                if( isNull( element ) )
175                {
176                    buffer.add( NULL_STRING );
177                }
178                else
179                {
180                    buffer.add( m_ElementStringConverter.toString( element ) );
181                }
182            }
183
184            retValue = buffer.toString();
185        }
186
187        //---* Done *----------------------------------------------------------
188        return retValue;
189    }   //  toString()
190}
191//  class ListStringConverter
192
193/*
194 *  End of File
195 */