/**
  * Returns the array of providers which meet the user supplied set of filters. The filter must be
  * supplied in one of two formats: <nl>
  * <li>CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE
  *
  *     <p>for example: "MessageDigest.SHA" The value associated with the key must be an empty
  *     string.
  * <li>CRYPTO_SERVICE_NAME.ALGORITHM_OR_TYPE ATTR_NAME:ATTR_VALUE
  *
  *     <p>for example: "Signature.MD2withRSA KeySize:512" where "KeySize:512" is the value of the
  *     filter map entry. </nl>
  *
  * @param filter case-insensitive filter.
  * @return the providers which meet the user supplied string filter {@code filter}. A {@code null}
  *     value signifies that none of the installed providers meets the filter specification.
  * @throws InvalidParameterException if an unusable filter is supplied.
  * @throws NullPointerException if {@code filter} is {@code null}.
  */
 public static synchronized Provider[] getProviders(Map<String, String> filter) {
   if (filter == null) {
     throw new NullPointerException();
   }
   if (filter.isEmpty()) {
     return null;
   }
   java.util.List<Provider> result = Services.getProvidersList();
   Set<Entry<String, String>> keys = filter.entrySet();
   Map.Entry<String, String> entry;
   for (Iterator<Entry<String, String>> it = keys.iterator(); it.hasNext(); ) {
     entry = it.next();
     String key = entry.getKey();
     String val = entry.getValue();
     String attribute = null;
     int i = key.indexOf(' ');
     int j = key.indexOf('.');
     if (j == -1) {
       throw new InvalidParameterException();
     }
     if (i == -1) { // <crypto_service>.<algorithm_or_type>
       if (val.length() != 0) {
         throw new InvalidParameterException();
       }
     } else { // <crypto_service>.<algorithm_or_type> <attribute_name>
       if (val.length() == 0) {
         throw new InvalidParameterException();
       }
       attribute = key.substring(i + 1);
       if (attribute.trim().length() == 0) {
         throw new InvalidParameterException();
       }
       key = key.substring(0, i);
     }
     String serv = key.substring(0, j);
     String alg = key.substring(j + 1);
     if (serv.length() == 0 || alg.length() == 0) {
       throw new InvalidParameterException();
     }
     Provider p;
     for (int k = 0; k < result.size(); k++) {
       try {
         p = result.get(k);
       } catch (IndexOutOfBoundsException e) {
         break;
       }
       if (!p.implementsAlg(serv, alg, attribute, val)) {
         result.remove(p);
         k--;
       }
     }
   }
   if (result.size() > 0) {
     return result.toArray(new Provider[result.size()]);
   }
   return null;
 }
 /**
  * Insert the given {@code Provider} at the specified {@code position}. The positions define the
  * preference order in which providers are searched for requested algorithms.
  *
  * @param provider the provider to insert.
  * @param position the position (starting from 1).
  * @return the actual position or {@code -1} if the given {@code provider} was already in the
  *     list. The actual position may be different from the desired position.
  */
 public static synchronized int insertProviderAt(Provider provider, int position) {
   // check that provider is not already
   // installed, else return -1; if (position <1) or (position > max
   // position) position = max position + 1; insert provider, shift up
   // one position for next providers; Note: The position is 1-based
   if (getProvider(provider.getName()) != null) {
     return -1;
   }
   int result = Services.insertProviderAt(provider, position);
   renumProviders();
   return result;
 }
  /**
   * Removes the {@code Provider} with the specified name form the collection of providers. If the
   * the {@code Provider} with the specified name is removed, all provider at a greater position are
   * shifted down one position.
   *
   * <p>Returns silently if {@code name} is {@code null} or no provider with the specified name is
   * installed.
   *
   * @param name the name of the provider to remove.
   */
  public static synchronized void removeProvider(String name) {
    // It is not clear from spec.:
    // 1. if name is null, should we checkSecurityAccess or not?
    //    throw SecurityException or not?
    // 2. as 1 but provider is not installed
    // 3. behavior if name is empty string?

    Provider p;
    if ((name == null) || (name.length() == 0)) {
      return;
    }
    p = getProvider(name);
    if (p == null) {
      return;
    }
    Services.removeProvider(p.getProviderNumber());
    renumProviders();
    p.setProviderNumber(-1);
  }
 /** Update sequence numbers of all providers. */
 private static void renumProviders() {
   Provider[] p = Services.getProviders();
   for (int i = 0; i < p.length; i++) {
     p[i].setProviderNumber(i + 1);
   }
 }
 /**
  * Returns the {@code Provider} with the specified name. Returns {@code null} if name is {@code
  * null} or no provider with the specified name is installed.
  *
  * @param name the name of the requested provider.
  * @return the provider with the specified name, maybe {@code null}.
  */
 public static synchronized Provider getProvider(String name) {
   return Services.getProvider(name);
 }
 /**
  * Returns an array containing all installed providers. The providers are ordered according their
  * preference order.
  *
  * @return an array containing all installed providers.
  */
 public static synchronized Provider[] getProviders() {
   return Services.getProviders();
 }