AsDelegateProvider.java

  1. /*
  2.  * *********************************************************************************************************************
  3.  *
  4.  * TheseFoolishThings: Miscellaneous utilities
  5.  * http://tidalwave.it/projects/thesefoolishthings
  6.  *
  7.  * Copyright (C) 2009 - 2023 by Tidalwave s.a.s. (http://tidalwave.it)
  8.  *
  9.  * *********************************************************************************************************************
  10.  *
  11.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  12.  * the License. You may obtain a copy of the License at
  13.  *
  14.  *     http://www.apache.org/licenses/LICENSE-2.0
  15.  *
  16.  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  17.  * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
  18.  * specific language governing permissions and limitations under the License.
  19.  *
  20.  * *********************************************************************************************************************
  21.  *
  22.  * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
  23.  * git clone https://github.com/tidalwave-it/thesefoolishthings-src
  24.  *
  25.  * *********************************************************************************************************************
  26.  */
  27. package it.tidalwave.util.spi;

  28. import javax.annotation.Nonnull;
  29. import java.util.ArrayList;
  30. import java.util.Collection;
  31. import java.util.ServiceLoader;
  32. import it.tidalwave.util.LazySupplier;
  33. import lombok.AccessLevel;
  34. import lombok.NoArgsConstructor;

  35. /***********************************************************************************************************************
  36.  *
  37.  * @author  Fabrizio Giudici
  38.  *
  39.  **********************************************************************************************************************/
  40. public interface AsDelegateProvider
  41.   {
  42.     public static final LazySupplier<EmptyAsDelegateProvider> EMPTY_REF =
  43.             LazySupplier.of(EmptyAsDelegateProvider::new);

  44.     @NoArgsConstructor(access = AccessLevel.PRIVATE)
  45.     public static final class Locator
  46.       {
  47.         private static final LazySupplier<AsDelegateProvider> AS_DELEGATE_PROVIDER_REF =
  48.                 LazySupplier.of(Locator::findAsSpiProvider);

  49.         @Nonnull
  50.         public static AsDelegateProvider find()
  51.           {
  52.             return AS_DELEGATE_PROVIDER_REF.get();
  53.           }

  54.         @Nonnull
  55.         private static AsDelegateProvider findAsSpiProvider()
  56.           {
  57.             final var classLoader = Thread.currentThread().getContextClassLoader();
  58.             final var i =
  59.                     ServiceLoader.load(AsDelegateProvider.class, classLoader).iterator();

  60.             if (!i.hasNext())
  61.               {
  62.                 final var message = "No ServiceProvider for AsDelegateProvider found in a ServiceLoader - if " +
  63.                                     "you are running tests perhaps you should first call " +
  64.                                     "AsDelegateProvider.Locator.set(AsDelegateProvider.empty()) or " +
  65.                                     "AsDelegateProvider.Locator.set(new MockSimpleAsDelegateProvider()) or " +
  66.                                     "another appropriate AsDelegateProvider";
  67.                 throw new RuntimeException(message);
  68.               }

  69.             return i.next();
  70.           }

  71.         /***************************************************************************************************************
  72.          *
  73.          * <b>This method is for testing only.</b> Sets the global {@link AsDelegateProvider}.
  74.          *
  75.          * @param   provider    the provider
  76.          * @see     #reset()
  77.          *
  78.          **************************************************************************************************************/
  79.         public static void set (@Nonnull final AsDelegateProvider provider)
  80.           {
  81.             AS_DELEGATE_PROVIDER_REF.set(provider);
  82.           }

  83.         /***************************************************************************************************************
  84.          *
  85.          * <b>This method is for testing only.</b> Resets the global {@link AsDelegateProvider}; it must be called at
  86.          * the test completion whenever {@link #set(AsDelegateProvider)} has been called, to avoid polluting the
  87.          * context of further tests.
  88.          *
  89.          * @see     #set(AsDelegateProvider)
  90.          *
  91.          **************************************************************************************************************/
  92.         public static void reset()
  93.           {
  94.             AS_DELEGATE_PROVIDER_REF.clear();
  95.           }
  96.       }

  97.     /*******************************************************************************************************************
  98.      *
  99.      * Creates an {@link AsDelegate} for the given object
  100.      *
  101.      * @param     datum   the object
  102.      * @return            {@code AsDelegate}
  103.      *
  104.      ******************************************************************************************************************/
  105.     @Nonnull
  106.     public AsDelegate createAsDelegate (@Nonnull Object datum);

  107.     /*******************************************************************************************************************
  108.      *
  109.      * Returns an empty implementation. Useful for setting up test environment.
  110.      *
  111.      * <pre>
  112.      * AsDelegateProvider.Locator.set(AsDelegateProvider.empty());
  113.      * </pre>
  114.      *
  115.      * @return    the empty implementation
  116.      * @since     3.2-ALPHA-1
  117.      *
  118.      ******************************************************************************************************************/
  119.     @Nonnull
  120.     public static AsDelegateProvider empty()
  121.       {
  122.         return EMPTY_REF.get();
  123.       }

  124.     /*******************************************************************************************************************
  125.      *
  126.      ******************************************************************************************************************/
  127.     static class EmptyAsDelegateProvider implements AsDelegateProvider
  128.       {
  129.         @Override @Nonnull
  130.         public AsDelegate createAsDelegate (@Nonnull final Object owner)
  131.           {
  132.             return new AsDelegate()
  133.               {
  134.                 @Override @Nonnull
  135.                 public <T> Collection<T> as (@Nonnull final Class<? extends T> type)
  136.                   {
  137.                     return new ArrayList<>(); // must be mutable
  138.                   }
  139.               };
  140.           }
  141.       }
  142.   }