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.internal;
019
020import static org.apiguardian.api.API.Status.STABLE;
021import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument;
022
023import java.io.Serial;
024import java.util.Collection;
025import java.util.Comparator;
026import java.util.Map;
027import java.util.Set;
028import java.util.TreeSet;
029
030import org.apiguardian.api.API;
031import org.tquadrat.foundation.annotation.ClassVersion;
032import org.tquadrat.foundation.lang.Pair;
033import org.tquadrat.foundation.util.RangeMap;
034
035/**
036 *  The implementation of the interface
037 *  {@link RangeMap}.
038 *
039 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
040 *  @version $Id: RangeMapImpl.java 1080 2024-01-03 11:05:21Z tquadrat $
041 *  @since 0.0.7
042 *
043 *  @param <T>  The type of the mapped value.
044 *
045 *  @UMLGraph.link
046 */
047@ClassVersion( sourceVersion = "$Id: RangeMapImpl.java 1080 2024-01-03 11:05:21Z tquadrat $" )
048@API( status = STABLE, since = "0.0.7" )
049public sealed class RangeMapImpl<T> implements RangeMap<T>
050    permits FinalRangeMap
051{
052        /*------------*\
053    ====** Attributes **=======================================================
054        \*------------*/
055    /**
056     *  This flag determines if the limit belongs to the range or not.
057     *
058     *  @serial
059     */
060    private boolean m_Includes = false;
061
062    /**
063     *  The Ranges.
064     *
065     *  @serial
066     */
067    private final Set<Pair<Double, ? extends T>> m_Ranges = new TreeSet<>( Comparator.comparing( Pair::left ) );
068
069        /*------------------------*\
070    ====** Static Initialisations **===========================================
071        \*------------------------*/
072    /**
073     *  The serial version UID for objects of this class: {@value}.
074     *
075     *  @hidden
076     */
077    @Serial
078    private static final long serialVersionUID = -1094863662311849594L;
079
080        /*--------------*\
081    ====** Constructors **=====================================================
082        \*--------------*/
083    /**
084     *  Creates a new {@code RangeMap} instance. For this instance, the limit
085     *  does not belong to the range.
086     *
087     *  @see #RangeMapImpl(boolean)
088     */
089    public RangeMapImpl() { this( false ); }
090
091    /**
092     *  Creates a new {@code RangeMap} instance.
093     *
094     *  @param  includes    {@code true} if the limit belongs to the
095     *      range, {@code false} otherwise.
096     */
097    public RangeMapImpl( final boolean includes )
098    {
099        m_Includes = includes;
100    }   //  RangeMap()
101
102    /**
103     *  Creates a new {@code RangeMap} instance from an existing one.
104     *
105     *  @param  other   The other instance.
106     */
107    @SuppressWarnings( "UseOfConcreteClass" )
108    public RangeMapImpl( final RangeMapImpl<? extends T> other )
109    {
110        m_Includes = requireNonNullArgument( other, "other" ).m_Includes;
111        m_Ranges.addAll( other.m_Ranges );
112    }   //  RangeMap()
113
114    /**
115     *  Creates a new {@code RangeMap} instance from an instance of
116     *  {@link Map}.
117     *
118     *  @param  includes    {@code true} if the limit belongs to the
119     *      range, {@code false} otherwise.
120     *  @param  map   The map instance.
121     */
122    public RangeMapImpl( final boolean includes, final Map<? extends Number,? extends T> map )
123    {
124        this( includes );
125        requireNonNullArgument( map, "map" )
126            .forEach( ( key, value ) -> m_Ranges.add( new Pair<>( Double.valueOf( key.doubleValue() ), value ) ) );
127    }   //  RangeMap()
128
129    /**
130     *  Creates a new {@code RangeMap} instance from a list of
131     *  pairs.
132     *
133     *  @param  includes    {@code true} if the limit belongs to the
134     *      range, {@code false} otherwise.
135     *  @param  nvpList The list of entries.
136     */
137    public RangeMapImpl( final boolean includes, final Collection<? extends Map.Entry<? extends Number, ? extends T>> nvpList )
138    {
139        this( includes );
140        requireNonNullArgument( nvpList, "nvpList" ).forEach( e -> m_Ranges.add( new Pair<>( Double.valueOf( e.getKey().doubleValue() ), e.getValue() ) ) );
141    }   //  RangeMap()
142
143        /*---------*\
144    ====** Methods **==========================================================
145        \*---------*/
146    /**
147     *  {@inheritDoc}
148     */
149    @Override
150    public RangeMapImpl<T> addRange( final double key, final T value )
151    {
152        m_Ranges.add( new Pair<>( Double.valueOf( key ), requireNonNullArgument( value, "value" ) ) );
153
154        //---* Done *----------------------------------------------------------
155        return this;
156    }   //  addRange()
157
158    /**
159     *  {@inheritDoc}
160     */
161    @Override
162    public void clear() { m_Ranges.clear(); }
163
164    /**
165     *  {@inheritDoc}
166     */
167    @Override
168    public final RangeMap<T> copy( final boolean modifiable )
169    {
170        final var retValue = modifiable ? new RangeMapImpl<>( this ) : new FinalRangeMap<>( this );
171
172        //---* Done *----------------------------------------------------------
173        return retValue;
174    }   //  copy()
175
176    /**
177     *  {@inheritDoc}
178     */
179    @Override
180    public final Pair<Double,T> [] entries()
181    {
182        @SuppressWarnings( "unchecked" )
183        final Pair<Double,T> [] retValue = m_Ranges.toArray( Pair []::new );
184
185        //---* Done *----------------------------------------------------------
186        return retValue;
187    }   //  entries()
188
189    /**
190     *  {@inheritDoc}
191     */
192    @Override
193    public final T get( final double key )
194    {
195        if( m_Ranges.isEmpty() ) throw new IllegalStateException( "RangeMap does not have any entries" );
196
197        final var iterator = m_Ranges.iterator();
198        T retValue;
199        Pair<Double,? extends T> entry;
200        do
201        {
202            entry = iterator.next();
203            retValue = entry.right();
204        }
205        while( (m_Includes ? entry.left() < key : entry.left() <= key) && iterator.hasNext() );
206
207        //---* Done *----------------------------------------------------------
208        return retValue;
209    }   //  get()
210
211    /**
212     *  {@inheritDoc}
213     */
214    @Override
215    public final boolean isEmpty() { return m_Ranges.isEmpty(); }
216
217    /**
218     *  {@inheritDoc}
219     */
220    @Override
221    public RangeMap<T> removeRange( final double key )
222    {
223        m_Ranges.remove( new Pair<>( Double.valueOf( key ), (T) null ) );
224
225        //---* Done *----------------------------------------------------------
226        return this;
227    }   //  removeRange()
228
229    /**
230     *  {@inheritDoc}
231     */
232    @Override
233    public RangeMapImpl<T> setDefault( final T value )
234    {
235        final var entry = new Pair<>( Double.valueOf( Double.MAX_VALUE ), requireNonNullArgument( value, "defaultValue" ) );
236        if( !m_Ranges.add( entry ) )
237        {
238            m_Ranges.remove( entry );
239            m_Ranges.add( entry );
240        }
241
242        //---* Done *----------------------------------------------------------
243        return this;
244    }   //  setDefault()
245
246    /**
247     *  Returns an immutable copy of the given {@code RangeMapImpl}.
248     *
249     *  @param  <T> The type of the mapped value.
250     *  @param  other   The other Range map.
251     *  @return The immutable copy.
252     */
253    @SuppressWarnings( "UseOfConcreteClass" )
254    public static <T> RangeMapImpl<T> unmodifiableRangeMap( final RangeMapImpl<? extends T> other )
255    {
256        final RangeMapImpl<T> retValue = new FinalRangeMap<>( requireNonNullArgument( other, "other" ) );
257
258        //---* Done *----------------------------------------------------------
259        return retValue;
260    }   //  unmodifiableRangeMap()
261}
262//  class RangeMapImpl
263
264/*
265 *  End of File
266 */