content: /*******************************************************************************
   * Copyright 2014 Trevor Robinson
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *   http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   ******************************************************************************/
  package com.scurrilous.circe;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.EnumSet;
  import java.util.Iterator;
  import java.util.LinkedList;
  import java.util.ServiceLoader;
  import java.util.SortedMap;
  import java.util.TreeMap;
  
  /**
   * Static utility methods for discovering {@link HashProvider} instances.
   */
  public final class HashProviders {
  
      static final Collection<HashProvider> ALL_PROVIDERS = getAllProviders();
  
      private static Collection<HashProvider> getAllProviders() {
          final ServiceLoader<HashProvider> loader = ServiceLoader.load(HashProvider.class);
          final LinkedList<HashProvider> providers = new LinkedList<>();
          for (final HashProvider provider : loader)
              providers.add(provider);
          return Collections.unmodifiableList(new ArrayList<>(providers));
      }
  
      private HashProviders() {
      }
  
      /**
       * Returns an iterator over all known {@link HashProvider} instances.
       * 
       * @return an iterator over all HashProviders
       */
      public static Iterator<HashProvider> iterator() {
          return ALL_PROVIDERS.iterator();
      }
  
      /**
       * Returns the best hash provider supporting at least a stateful
       * implementation of a hash function with the given parameters.
       * 
       * @param params the parameters defining the hash function
       * @return the best hash provider for the given parameters
       * @throws UnsupportedOperationException if no provider supports the
       *             parameters
       */
      public static HashProvider best(HashParameters params) {
          return best(params, EnumSet.of(HashSupport.STATEFUL));
      }
  
      /**
       * Returns the best hash provider supporting at least the given flags for a
       * hash function with the given parameters.
       * 
       * @param params the parameters defining the hash function
       * @param required the required support flags for a provider to be
       *            considered
       * @return the best hash provider for the given parameters
       * @throws UnsupportedOperationException if no provider supports the
       *             parameters
       */
      public static HashProvider best(HashParameters params, EnumSet<HashSupport> required) {
          HashProvider result = null;
          EnumSet<HashSupport> resultSupport = null;
          for (final HashProvider provider : ALL_PROVIDERS) {
              final EnumSet<HashSupport> support = provider.querySupport(params);
              if (support.containsAll(required) &&
                      (result == null || HashSupport.compare(support, resultSupport) < 0)) {
                  result = provider;
                  resultSupport = support;
              }
          }
          if (result == null)
              throw new UnsupportedOperationException();
          return result;
      }
  
      /**
       * Returns a map of hash providers supporting at least a stateful
       * implementation of a hash function with the given parameters.
       * 
       * @param params the parameters defining the hash function
       * @return a sorted map of hash support flags to hash providers
       */
      public static SortedMap<EnumSet<HashSupport>, HashProvider> search(HashParameters params) {
          return search(params, EnumSet.of(HashSupport.STATEFUL));
      }
  
      /**
       * Returns a map of hash providers supporting at least the given flags for a
       * hash function with the given parameters.
       * 
       * @param params the parameters defining the hash function
       * @param required the required support flags for a provider to be included
       * @return a sorted map of hash support flags to hash providers
       */
      public static SortedMap<EnumSet<HashSupport>, HashProvider> search(HashParameters params,
              EnumSet<HashSupport> required) {
          final SortedMap<EnumSet<HashSupport>, HashProvider> result = new TreeMap<>(
                  new HashSupport.SetComparator());
          for (final HashProvider provider : ALL_PROVIDERS) {
              final EnumSet<HashSupport> support = provider.querySupport(params);
              if (support.containsAll(required))
                  result.put(support, provider);
          }
          return result;
      }
  }
  