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}