001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2016, 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 0.9
059 */
060public class DefaultServiceProvider extends ServiceProvider {
061  /** List of services loaded, per class. */
062  @SuppressWarnings("rawtypes")
063  private final Map<Class, List<Object>> servicesLoaded = new HashMap<Class, List<Object>>();
064
065  @SuppressWarnings("rawtypes")
066  private final Map<Class, QuantityFactory> QUANTITY_FACTORIES = new HashMap<Class, QuantityFactory>();
067
068  static final class ServiceCompare implements Comparator<Object> {
069    @Override
070    public int compare(Object o1, Object o2) {
071      Logger.getLogger(DefaultServiceProvider.class.getName()).log(FINER, "Comparing " + o1 + " and " + o2);
072      int prio1 = 0;
073      int prio2 = 0;
074
075      if (o1 instanceof IntPrioritySupplier) {
076        prio1 = ((IntPrioritySupplier) o1).getPriority();
077      }
078      if (o2 instanceof IntPrioritySupplier) {
079        prio2 = ((IntPrioritySupplier) o2).getPriority();
080      }
081      if (prio1 < prio2) {
082        return 1;
083      }
084      if (prio2 < prio1) {
085        return -1;
086      }
087      return o2.getClass().getName().compareTo(o1.getClass().getName()); // TODO
088      // maybe
089      // use
090      // something
091      // else
092      // here?
093    }
094  }
095
096  private static final Comparator<Object> SERVICE_COMPARATOR = new ServiceCompare();
097
098  @Override
099  public int getPriority() {
100    return 10;
101  }
102
103  /**
104   * Loads and registers services.
105   *
106   * @param serviceType
107   *          The service type.
108   * @param <T>
109   *          the concrete type.
110   * @return the items found, never {@code null}.
111   */
112  protected <T> List<T> getServices(final Class<T> serviceType) {
113    @SuppressWarnings("unchecked")
114    List<T> found = (List<T>) servicesLoaded.get(serviceType);
115    if (found != null) {
116      return found;
117    }
118
119    return loadServices(serviceType);
120  }
121
122  protected <T> T getService(Class<T> serviceType) {
123    List<T> servicesFound = getServices(serviceType);
124    if (servicesFound.isEmpty()) {
125      return null;
126    }
127    return servicesFound.get(0);
128  }
129
130  /**
131   * Loads and registers services.
132   *
133   * @param serviceType
134   *          The service type.
135   * @param <T>
136   *          the concrete type.
137   *
138   * @return the items found, never {@code null}.
139   */
140  @SuppressWarnings("unchecked")
141  private <T> List<T> loadServices(final Class<T> serviceType) {
142    final List<T> services = new ArrayList<T>();
143    try {
144      for (T t : ServiceLoader.load(serviceType)) {
145        services.add(t);
146      }
147      Collections.sort(services, SERVICE_COMPARATOR);
148      final List<T> previousServices = (List<T>) servicesLoaded.put(serviceType, (List<Object>) services);
149      return list(previousServices != null ? previousServices.iterator() : services.iterator());
150    } catch (Exception e) {
151      Logger.getLogger(DefaultServiceProvider.class.getName()).log(WARNING, "Error loading services of type " + serviceType, e);
152      Collections.sort(services, SERVICE_COMPARATOR);
153      return services;
154    }
155  }
156
157  int compareTo(ServiceProvider o) {
158    return compare(getPriority(), o.getPriority());
159  }
160
161  /**
162   * Compares two {@code int} values numerically. The value returned is identical to what would be returned by:
163   * 
164   * <pre>
165   * Integer.valueOf(x).compareTo(Integer.valueOf(y))
166   * </pre>
167   *
168   * @param x
169   *          the first {@code int} to compare
170   * @param y
171   *          the second {@code int} to compare
172   * @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}
173   */
174  private static int compare(int x, int y) {
175    return (x < y) ? -1 : ((x == y) ? 0 : 1);
176  }
177
178  /**
179   * Returns an array list containing the elements returned by the specified iterator in the order they are returned by the enumeration. This method
180   * provides interoperability between legacy APIs that return enumerations and new APIs that require collections.
181   *
182   * @param e
183   *          enumeration providing elements for the returned array list
184   * @return an array list containing the elements returned by the specified enumeration.
185   * @since 1.4
186   * @see Enumeration
187   * @see ArrayList
188   */
189  private static <T> ArrayList<T> list(Iterator<T> i) {
190    ArrayList<T> l = new ArrayList<>();
191    while (i.hasNext())
192      l.add(i.next());
193    return l;
194  }
195
196  @Override
197  public SystemOfUnitsService getSystemOfUnitsService() {
198    return getService(SystemOfUnitsService.class);
199  }
200
201  @Override
202  public UnitFormatService getUnitFormatService() {
203    return getService(UnitFormatService.class);
204  }
205
206  /**
207   * Return a factory for this quantity
208   * 
209   * @param quantity
210   *          the quantity type
211   * @return the {@link QuantityFactory}
212   * @throws NullPointerException
213   */
214  @Override
215  @SuppressWarnings("unchecked")
216  public final <Q extends Quantity<Q>> QuantityFactory<Q> getQuantityFactory(Class<Q> quantity) {
217    if (quantity == null)
218      throw new NullPointerException();
219    if (!QUANTITY_FACTORIES.containsKey(quantity)) {
220      synchronized (QUANTITY_FACTORIES) {
221        QUANTITY_FACTORIES.put(quantity, DefaultQuantityFactory.getInstance(quantity));
222      }
223    }
224    return QUANTITY_FACTORIES.get(quantity);
225  }
226}