001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil, V2COM.
004 *
005 * All rights reserved.
006 *
007 * Redistribution and use in source and binary forms, with or without modification,
008 * are permitted provided that the following conditions are met:
009 *
010 * 1. Redistributions of source code must retain the above copyright notice,
011 *    this list of conditions and the following disclaimer.
012 *
013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
014 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
015 *
016 * 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products
017 *    derived from this software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package tec.units.ri.spi;
031
032import static java.util.logging.Level.*;
033
034import java.util.ArrayList;
035import java.util.Collections;
036import java.util.Comparator;
037import java.util.Enumeration;
038import java.util.HashMap;
039import java.util.Iterator;
040import java.util.List;
041import java.util.Map;
042import java.util.ServiceLoader;
043import java.util.logging.Logger;
044
045import javax.measure.Quantity;
046import javax.measure.spi.QuantityFactory;
047import javax.measure.spi.ServiceProvider;
048import javax.measure.spi.SystemOfUnitsService;
049import javax.measure.spi.UnitFormatService;
050
051import tec.units.ri.quantity.DefaultQuantityFactory;
052import tec.uom.lib.common.function.IntPrioritySupplier;
053
054/**
055 * This class extends the {@link ServiceProvider} class and hereby uses the JDK {@link java.util.ServiceLoader} to load the services required.
056 *
057 * @author Werner Keil
058 * @version 1.0
059 * @since 1.0
060 */
061public class DefaultServiceProvider extends ServiceProvider {
062  /** List of services loaded, per class. */
063  @SuppressWarnings("rawtypes")
064  private final Map<Class, List<Object>> servicesLoaded = new HashMap<Class, List<Object>>();
065
066  @SuppressWarnings("rawtypes")
067  private final Map<Class, QuantityFactory> QUANTITY_FACTORIES = new HashMap<Class, QuantityFactory>();
068
069  static final class ServiceCompare implements Comparator<Object> {
070    @Override
071    public int compare(Object o1, Object o2) {
072      Logger.getLogger(DefaultServiceProvider.class.getName()).log(FINER, "Comparing " + o1 + " and " + o2);
073      int prio1 = 0;
074      int prio2 = 0;
075
076      if (o1 instanceof IntPrioritySupplier) {
077        prio1 = ((IntPrioritySupplier) o1).getPriority();
078      }
079      if (o2 instanceof IntPrioritySupplier) {
080        prio2 = ((IntPrioritySupplier) o2).getPriority();
081      }
082      if (prio1 < prio2) {
083        return 1;
084      }
085      if (prio2 < prio1) {
086        return -1;
087      }
088      return o2.getClass().getName().compareTo(o1.getClass().getName()); // TODO
089      // maybe
090      // use
091      // something
092      // else
093      // here?
094    }
095  }
096
097  private static final Comparator<Object> SERVICE_COMPARATOR = new ServiceCompare();
098
099  @Override
100  public int getPriority() {
101    return 10;
102  }
103
104  /**
105   * Loads and registers services.
106   *
107   * @param serviceType
108   *          The service type.
109   * @param <T>
110   *          the concrete type.
111   * @return the items found, never {@code null}.
112   */
113  protected <T> List<T> getServices(final Class<T> serviceType) {
114    @SuppressWarnings("unchecked")
115    List<T> found = (List<T>) servicesLoaded.get(serviceType);
116    if (found != null) {
117      return found;
118    }
119
120    return loadServices(serviceType);
121  }
122
123  protected <T> T getService(Class<T> serviceType) {
124    List<T> servicesFound = getServices(serviceType);
125    if (servicesFound.isEmpty()) {
126      return null;
127    }
128    return servicesFound.get(0);
129  }
130
131  /**
132   * Loads and registers services.
133   *
134   * @param serviceType
135   *          The service type.
136   * @param <T>
137   *          the concrete type.
138   *
139   * @return the items found, never {@code null}.
140   */
141  @SuppressWarnings("unchecked")
142  private <T> List<T> loadServices(final Class<T> serviceType) {
143    final List<T> services = new ArrayList<T>();
144    try {
145      for (T t : ServiceLoader.load(serviceType)) {
146        services.add(t);
147      }
148      Collections.sort(services, SERVICE_COMPARATOR);
149      final List<T> previousServices = (List<T>) servicesLoaded.put(serviceType, (List<Object>) services);
150      return list(previousServices != null ? previousServices.iterator() : services.iterator());
151    } catch (Exception e) {
152      Logger.getLogger(DefaultServiceProvider.class.getName()).log(WARNING, "Error loading services of type " + serviceType, e);
153      Collections.sort(services, SERVICE_COMPARATOR);
154      return services;
155    }
156  }
157
158  int compareTo(ServiceProvider o) {
159    return compare(getPriority(), o.getPriority());
160  }
161
162  /**
163   * Compares two {@code int} values numerically. The value returned is identical to what would be returned by:
164   * 
165   * <pre>
166   * Integer.valueOf(x).compareTo(Integer.valueOf(y))
167   * </pre>
168   *
169   * @param x
170   *          the first {@code int} to compare
171   * @param y
172   *          the second {@code int} to compare
173   * @return the value {@code 0} if {@code x == y}; a value less than {@code 0} if {@code x < y}; and a value greater than {@code 0} if {@code x > y}
174   */
175  private static int compare(int x, int y) {
176    return (x < y) ? -1 : ((x == y) ? 0 : 1);
177  }
178
179  /**
180   * Returns an array list containing the elements returned by the specified iterator in the order they are returned by the enumeration. This method
181   * provides interoperability between legacy APIs that return enumerations and new APIs that require collections.
182   *
183   * @param e
184   *          enumeration providing elements for the returned array list
185   * @return an array list containing the elements returned by the specified enumeration.
186   * @since 1.4
187   * @see Enumeration
188   * @see ArrayList
189   */
190  private static <T> ArrayList<T> list(Iterator<T> i) {
191    ArrayList<T> l = new ArrayList<>();
192    while (i.hasNext())
193      l.add(i.next());
194    return l;
195  }
196
197  @Override
198  public SystemOfUnitsService getSystemOfUnitsService() {
199    return getService(SystemOfUnitsService.class);
200  }
201
202  @Override
203  public UnitFormatService getUnitFormatService() {
204    return getService(UnitFormatService.class);
205  }
206
207  /**
208   * Return a factory for this quantity
209   * 
210   * @param quantity
211   *          the quantity type
212   * @return the {@link QuantityFactory}
213   * @throws NullPointerException
214   */
215  @Override
216  @SuppressWarnings("unchecked")
217  public final <Q extends Quantity<Q>> QuantityFactory<Q> getQuantityFactory(Class<Q> quantity) {
218    if (quantity == null)
219      throw new NullPointerException();
220    if (!QUANTITY_FACTORIES.containsKey(quantity)) {
221      synchronized (QUANTITY_FACTORIES) {
222        QUANTITY_FACTORIES.put(quantity, DefaultQuantityFactory.getInstance(quantity));
223      }
224    }
225    return QUANTITY_FACTORIES.get(quantity);
226  }
227}