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}