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 java.util.Collections.emptyIterator; 021import static java.util.Collections.emptyList; 022import static java.util.Collections.emptyListIterator; 023import static java.util.Spliterators.emptySpliterator; 024import static org.apiguardian.api.API.Status.INTERNAL; 025import static org.tquadrat.foundation.lang.CommonConstants.EMPTY_Object_ARRAY; 026import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 027 028import java.util.Collection; 029import java.util.Comparator; 030import java.util.HashSet; 031import java.util.Iterator; 032import java.util.List; 033import java.util.ListIterator; 034import java.util.Spliterator; 035import java.util.function.Consumer; 036import java.util.function.Predicate; 037import java.util.function.Supplier; 038import java.util.function.UnaryOperator; 039 040import org.apiguardian.api.API; 041import org.tquadrat.foundation.annotation.ClassVersion; 042import org.tquadrat.foundation.lang.Lazy; 043import org.tquadrat.foundation.util.LazyList; 044 045/** 046 * The implementation for 047 * {@link LazyList}. 048 * 049 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 050 * @version $Id: LazyListImpl.java 1060 2023-09-24 19:21:40Z tquadrat $ 051 * @since 0.0.5 052 * 053 * @param <E> The type of elements in this list. 054 * 055 * @UMLGraph.link 056 */ 057@SuppressWarnings( "OverlyComplexClass" ) 058@ClassVersion( sourceVersion = "$Id: LazyListImpl.java 1060 2023-09-24 19:21:40Z tquadrat $" ) 059@API( status = INTERNAL, since = "0.0.5" ) 060public final class LazyListImpl<E> implements LazyList<E> 061{ 062 /*------------*\ 063 ====** Attributes **======================================================= 064 \*------------*/ 065 /** 066 * The holder for the real list. 067 */ 068 private final Lazy<? extends List<E>> m_Holder; 069 070 /** 071 * The flag that indicates whether the provided supplier will put values 072 * to the list on initialisation. 073 */ 074 private final boolean m_SupplierPopulates; 075 076 /*--------------*\ 077 ====** Constructors **===================================================== 078 \*--------------*/ 079 /** 080 * Creates a new {@code LazyListImpl} instance. 081 * 082 * @param doPopulate {@code true} if the provided supplier will put 083 * values to the list on initialisation, {@code false} if it will 084 * create an empty list. 085 * @param supplier The supplier that initialises the internal list 086 * for this instance when it is first needed. 087 */ 088 public LazyListImpl( final boolean doPopulate, final Supplier<? extends List<E>> supplier ) 089 { 090 m_Holder = Lazy.use( supplier ); 091 m_SupplierPopulates = doPopulate; 092 } // LazyListImpl() 093 094 /** 095 * Creates a new {@code LazyListImpl} instance that is initialised with the 096 * given value. 097 * 098 * @param value The initialisation value. 099 */ 100 public LazyListImpl( final List<E> value ) 101 { 102 m_Holder = Lazy.of( value ); 103 m_SupplierPopulates = true; // … although this is redundant … 104 } // LazyListImpl() 105 106 /*---------*\ 107 ====** Methods **========================================================== 108 \*---------*/ 109 /** 110 * {@inheritDoc} 111 */ 112 @Override 113 public final boolean add( final E e ) { return m_Holder.get().add( e ); } 114 115 /** 116 * {@inheritDoc} 117 */ 118 @Override 119 public final void add( final int index, final E element ) 120 { 121 if( !isPresent() && !m_SupplierPopulates && (index != 0) ) throw new IndexOutOfBoundsException( index ); 122 123 m_Holder.get().add( index, element ); 124 } // add() 125 126 /** 127 * {@inheritDoc} 128 */ 129 @Override 130 public final boolean addAll( final Collection<? extends E> c ) 131 { 132 final var retValue = !c.isEmpty() && m_Holder.get().addAll( c ); 133 134 //---* Done *---------------------------------------------------------- 135 return retValue; 136 } // addAll() 137 138 /** 139 * {@inheritDoc} 140 */ 141 @Override 142 public final boolean addAll( final int index, final Collection<? extends E> c ) 143 { 144 final var retValue = !c.isEmpty() && m_Holder.get().addAll( index, c ); 145 146 //---* Done *---------------------------------------------------------- 147 return retValue; 148 } // addAll() 149 150 /** 151 * {@inheritDoc} 152 */ 153 @Override 154 public final void clear() 155 { 156 if( m_SupplierPopulates ) init(); 157 m_Holder.ifPresent( List::clear ); 158 } // clear() 159 160 /** 161 * {@inheritDoc} 162 */ 163 @Override 164 public final boolean contains( final Object o ) 165 { 166 @SuppressWarnings( "unlikely-arg-type" ) 167 final var retValue = (m_Holder.isPresent() || m_SupplierPopulates) && m_Holder.get().contains( o ); 168 169 //---* Done *---------------------------------------------------------- 170 return retValue; 171 } // contains() 172 173 /** 174 * {@inheritDoc} 175 */ 176 @Override 177 public final boolean containsAll( final Collection<?> c ) 178 { 179 java.util.Objects.requireNonNull( c ); 180 @SuppressWarnings( "unlikely-arg-type" ) 181 final var retValue = (m_Holder.isPresent() || m_SupplierPopulates) && new HashSet<>( m_Holder.get() ).containsAll( c ); 182 183 //---* Done *---------------------------------------------------------- 184 return retValue; 185 } // contains() 186 187 /** 188 * {@inheritDoc} 189 */ 190 @Override 191 public final boolean equals( final Object o ) 192 { 193 var retValue = o == this; 194 if( !retValue && (o instanceof final List<?> other) ) 195 { 196 /* 197 * Refer to the Javadoc for List::equals: Lists are considered 198 * equal if they have the same contents. 199 */ 200 if( isPresent() || m_SupplierPopulates ) 201 { 202 retValue = m_Holder.get().equals( other ); 203 } 204 else 205 { 206 retValue = other.isEmpty(); 207 } 208 } 209 210 //---* Done *---------------------------------------------------------- 211 return retValue; 212 } // equals() 213 214 /** 215 * {@inheritDoc} 216 */ 217 @Override 218 public final void forEach( final Consumer<? super E> action ) 219 { 220 if( m_SupplierPopulates ) init(); 221 java.util.Objects.requireNonNull( action ); 222 m_Holder.ifPresent( list -> list.forEach( action ) ); 223 } // forEach() 224 225 /** 226 * {@inheritDoc} 227 */ 228 @Override 229 public final E get( final int index ) 230 { 231 if( m_SupplierPopulates ) init(); 232 final var retValue = m_Holder.orElseThrow( () -> new IndexOutOfBoundsException( index ) ).get( index ); 233 234 //---* Done *---------------------------------------------------------- 235 return retValue; 236 } // get() 237 238 /** 239 * {@inheritDoc} 240 */ 241 @Override 242 public final void ifPresent( final Consumer<? super List<E>> consumer ) 243 { 244 if( m_SupplierPopulates ) init(); 245 //noinspection FunctionalExpressionCanBeFolded 246 m_Holder.ifPresent( requireNonNullArgument( consumer, "consumer" )::accept ); 247 } // ifPresent() 248 249 /** 250 * {@inheritDoc} 251 */ 252 @Override 253 public final int indexOf( final Object o ) 254 { 255 @SuppressWarnings( "unlikely-arg-type" ) 256 final var retValue = m_Holder.isPresent() || m_SupplierPopulates ? m_Holder.get().indexOf( o ) : -1; 257 258 //---* Done *---------------------------------------------------------- 259 return retValue; 260 } // indexOf() 261 262 /** 263 * {@inheritDoc} 264 */ 265 @Override 266 public final int hashCode() { return m_Holder.hashCode(); } 267 268 /** 269 * {@inheritDoc} 270 */ 271 @Override 272 public final void init() { m_Holder.get(); } 273 274 /** 275 * {@inheritDoc} 276 */ 277 @Override 278 public final boolean isEmpty() 279 { 280 final var retValue = (!m_Holder.isPresent() && !m_SupplierPopulates) || m_Holder.get().isEmpty(); 281 282 //---* Done *---------------------------------------------------------- 283 return retValue; 284 } // isEmpty() 285 286 /** 287 * {@inheritDoc} 288 */ 289 @Override 290 public final boolean isPresent() { return m_Holder.isPresent(); } 291 292 /** 293 * {@inheritDoc} 294 */ 295 @Override 296 public final Iterator<E> iterator() 297 { 298 if( m_SupplierPopulates ) init(); 299 final var retValue = m_Holder.map( List::iterator ).orElse( emptyIterator() ); 300 301 //---* Done *---------------------------------------------------------- 302 return retValue; 303 } // iterator() 304 305 /** 306 * {@inheritDoc} 307 */ 308 @Override 309 public final int lastIndexOf( final Object o ) 310 { 311 @SuppressWarnings( "unlikely-arg-type" ) 312 final var retValue = m_Holder.isPresent() || m_SupplierPopulates ? m_Holder.get().lastIndexOf( o ) : -1; 313 314 //---* Done *---------------------------------------------------------- 315 return retValue; 316 } // lastIndexOf() 317 318 /** 319 * {@inheritDoc} 320 */ 321 @Override 322 public final ListIterator<E> listIterator() 323 { 324 if( m_SupplierPopulates ) init(); 325 final var retValue = m_Holder.map( List::listIterator ).orElse( emptyListIterator() ); 326 327 //---* Done *---------------------------------------------------------- 328 return retValue; 329 } // listIterator() 330 331 /** 332 * {@inheritDoc} 333 */ 334 @Override 335 public final ListIterator<E> listIterator( final int index ) 336 { 337 if( m_SupplierPopulates ) init(); 338 final var retValue = index == 0 ? listIterator() : m_Holder.orElseThrow( () -> new IndexOutOfBoundsException( index ) ).listIterator( index ); 339 340 //---* Done *---------------------------------------------------------- 341 return retValue; 342 } // listIterator() 343 344 /** 345 * {@inheritDoc} 346 */ 347 @SuppressWarnings( "unlikely-arg-type" ) 348 @Override 349 public final boolean remove( final Object o ) 350 { 351 final var retValue = (m_Holder.isPresent() || m_SupplierPopulates) && m_Holder.get().remove( o ); 352 353 //---* Done *---------------------------------------------------------- 354 return retValue; 355 } // remove() 356 357 /** 358 * {@inheritDoc} 359 */ 360 @Override 361 public final E remove( final int index ) 362 { 363 if( m_SupplierPopulates ) init(); 364 final var retValue = m_Holder.orElseThrow( () -> new IndexOutOfBoundsException( index ) ).remove( index ); 365 366 //---* Done *---------------------------------------------------------- 367 return retValue; 368 } // remove() 369 370 /** 371 * {@inheritDoc} 372 */ 373 @SuppressWarnings( "unlikely-arg-type" ) 374 @Override 375 public final boolean removeAll( final Collection<?> c ) 376 { 377 final var retValue = (m_Holder.isPresent() || m_SupplierPopulates) && m_Holder.get().removeAll( c ); 378 379 //---* Done *---------------------------------------------------------- 380 return retValue; 381 } // removeAll() 382 383 /** 384 * {@inheritDoc} 385 */ 386 @Override 387 public final boolean removeIf( final Predicate<? super E> filter ) 388 { 389 final var retValue = (m_Holder.isPresent() || m_SupplierPopulates) && m_Holder.get().removeIf( filter ); 390 391 //---* Done *---------------------------------------------------------- 392 return retValue; 393 } // removeIf() 394 395 /** 396 * {@inheritDoc} 397 */ 398 @Override 399 public final void replaceAll( final UnaryOperator<E> operator ) 400 { 401 if( m_SupplierPopulates ) init(); 402 m_Holder.ifPresent( list -> list.replaceAll( operator ) ); 403 } // replaceAll() 404 405 /** 406 * {@inheritDoc} 407 */ 408 @Override 409 public final boolean retainAll( final Collection<?> c ) 410 { 411 @SuppressWarnings( "unlikely-arg-type" ) 412 final var retValue = (m_Holder.isPresent() || m_SupplierPopulates) && m_Holder.get().retainAll( c ); 413 414 //---* Done *---------------------------------------------------------- 415 return retValue; 416 } // retainAll() 417 418 /** 419 * {@inheritDoc} 420 */ 421 @Override 422 public final E set( final int index, final E element ) 423 { 424 if( m_SupplierPopulates ) init(); 425 final var retValue = m_Holder.orElseThrow( () -> new IndexOutOfBoundsException( index ) ).set( index, element ); 426 427 //---* Done *---------------------------------------------------------- 428 return retValue; 429 } // set() 430 431 /** 432 * {@inheritDoc} 433 */ 434 @Override 435 public final int size() 436 { 437 final var retValue = m_Holder.isPresent() || m_SupplierPopulates ? m_Holder.get().size() : 0; 438 439 //---* Done *---------------------------------------------------------- 440 return retValue; 441 } // size() 442 443 /** 444 * {@inheritDoc} 445 */ 446 @Override 447 public final void sort( final Comparator<? super E> c ) 448 { 449 if( m_SupplierPopulates ) init(); 450 m_Holder.ifPresent( list -> list.sort( c ) ); 451 } // sort() 452 453 /** 454 * {@inheritDoc} 455 */ 456 @Override 457 public final Spliterator<E> spliterator() 458 { 459 if( m_SupplierPopulates ) init(); 460 final var retValue = m_Holder.map( List::spliterator ).orElse( emptySpliterator() ); 461 462 //---* Done *---------------------------------------------------------- 463 return retValue; 464 } // spliterator() 465 466 /** 467 * {@inheritDoc} 468 */ 469 @SuppressWarnings( "NewExceptionWithoutArguments" ) 470 @Override 471 public final List<E> subList( final int fromIndex, final int toIndex ) 472 { 473 /* 474 * From the description of List.subList(): "Returns a view of the 475 * portion of this list between the specified fromIndex, inclusive, and 476 * toIndex, exclusive. (If fromIndex and toIndex are equal, the 477 * returned list is empty.) 478 * 479 * The implementation in AbstractList.subList() uses a method 480 * subListRangeCheck() for the validation of the index values; that 481 * method is implemented like this: 482 * 483 * static void subListRangeCheck( int fromIndex, int toIndex, int size) 484 * { 485 * if( fromIndex < 0 ) throw new IndexOutOfBoundsException( "fromIndex = " + fromIndex ); 486 * if( toIndex > size ) throw new IndexOutOfBoundsException( "toIndex = " + toIndex ); 487 * if( fromIndex > toIndex ) throw new IllegalArgumentException( "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")" ); 488 * } 489 * 490 * From this, the sublist of an empty list is an empty list, if the 491 * fromIndex and the toIndex are both 0. 492 */ 493 final List<E> retValue; 494 if( (fromIndex == toIndex) && (toIndex == 0) ) 495 { 496 retValue = emptyList(); 497 } 498 else 499 { 500 if( m_SupplierPopulates ) init(); 501 retValue = m_Holder.orElseThrow( IndexOutOfBoundsException::new ).subList( fromIndex, toIndex ); 502 } 503 504 //---* Done *---------------------------------------------------------- 505 return retValue; 506 } // subList() 507 508 /** 509 * {@inheritDoc} 510 */ 511 @Override 512 public final Object [] toArray() 513 { 514 if( m_SupplierPopulates ) init(); 515 final var retValue = m_Holder.map( List::toArray ).orElse( EMPTY_Object_ARRAY ); 516 517 //---* Done *---------------------------------------------------------- 518 return retValue; 519 } // toArray() 520 521 /** 522 * {@inheritDoc} 523 */ 524 @Override 525 public final <T> T [] toArray( final T [] a ) 526 { 527 var retValue = a; 528 if( m_Holder.isPresent() || m_SupplierPopulates ) 529 { 530 retValue = m_Holder.get().toArray( a ); 531 } 532 else 533 { 534 if( retValue.length > 0 ) 535 { 536 //noinspection AssignmentToNull 537 retValue [0] = null; 538 } 539 } 540 541 //---* Done *---------------------------------------------------------- 542 return retValue; 543 } // toArray() 544 545 /** 546 * {@inheritDoc} 547 */ 548 @Override 549 public final String toString() 550 { 551 final var retValue = m_Holder.isPresent() || m_SupplierPopulates ? m_Holder.getAsString() : "[Not initialized]"; 552 553 //---* Done *---------------------------------------------------------- 554 return retValue; 555 } // toString() 556} 557// class LazyListImpl 558 559/* 560 * End of File 561 */