001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2018, Jean-Marie Dautelle, Werner Keil, Otavio Santana.
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-385, Indriya nor the names of their 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 tech.units.indriya.internal;
031
032import javax.measure.Quantity;
033import javax.measure.spi.FormatService;
034import javax.measure.spi.QuantityFactory;
035import javax.measure.spi.ServiceProvider;
036import javax.measure.spi.SystemOfUnitsService;
037import javax.measure.spi.UnitFormatService;
038
039import tech.units.indriya.quantity.DefaultQuantityFactory;
040import java.util.ArrayList;
041import java.util.Collections;
042import java.util.Comparator;
043import java.util.List;
044import java.util.Map;
045import java.util.ServiceLoader;
046import java.util.concurrent.ConcurrentHashMap;
047import java.util.logging.Level;
048import java.util.logging.Logger;
049
050/**
051 * This class extends the {@link javax.measure.spi.ServiceProvider} class and
052 * hereby uses the JDK {@link java.util.ServiceLoader} to load the required
053 * services.
054 *
055 * @author Werner Keil
056 * @version 1.2
057 * @since 1.0
058 */
059public class DefaultServiceProvider extends ServiceProvider implements Comparable<ServiceProvider> {
060        /**
061         * List of services loaded, per class.
062         */
063        @SuppressWarnings("rawtypes")
064        private final Map<Class, List<Object>> servicesLoaded = new ConcurrentHashMap<>();
065
066        private static final Comparator<Object> SERVICE_COMPARATOR = DefaultServiceProvider::compareServices;
067
068        @SuppressWarnings("rawtypes")
069        private final Map<Class, QuantityFactory> QUANTITY_FACTORIES = new ConcurrentHashMap<>();
070
071        /**
072         * Returns a priority value of 10.
073         *
074         * @return 10, overriding the default provider.
075         */
076        @Override
077        public int getPriority() {
078                return 10;
079        }
080
081        /**
082         * Loads and registers services.
083         *
084         * @param serviceType
085         *            The service type.
086         * @param <T>
087         *            the concrete type.
088         * @return the items found, never {@code null}.
089         */
090        protected <T> List<T> getServices(final Class<T> serviceType) {
091                @SuppressWarnings("unchecked")
092                List<T> found = (List<T>) servicesLoaded.get(serviceType);
093                if (found != null) {
094                        return found;
095                }
096                return loadServices(serviceType);
097        }
098
099        protected <T> T getService(Class<T> serviceType) {
100                List<T> services = getServices(serviceType);
101                if (services.isEmpty()) {
102                        return null;
103                }
104                return services.get(0);
105        }
106
107        private static int compareServices(Object o1, Object o2) {
108                int prio1 = 0;
109                int prio2 = 0;
110                if (prio1 < prio2) {
111                        return 1;
112                }
113                if (prio2 < prio1) {
114                        return -1;
115                }
116                return o2.getClass().getSimpleName().compareTo(o1.getClass().getSimpleName());
117        }
118
119        /**
120         * Loads and registers services.
121         *
122         * @param serviceType
123         *            The service type.
124         * @param <T>
125         *            the concrete type.
126         * @return the items found, never {@code null}.
127         */
128        private <T> List<T> loadServices(final Class<T> serviceType) {
129                List<T> services = new ArrayList<>();
130                try {
131                        for (T t : ServiceLoader.load(serviceType)) {
132                                services.add(t);
133                        }
134                        Collections.sort(services, SERVICE_COMPARATOR);
135                        @SuppressWarnings("unchecked")
136                        final List<T> previousServices = (List<T>) servicesLoaded.putIfAbsent(serviceType, (List<Object>) services);
137                        return Collections.unmodifiableList(previousServices != null ? previousServices : services);
138                } catch (Exception e) {
139                        Logger.getLogger(DefaultServiceProvider.class.getName()).log(Level.WARNING,
140                                        "Error loading services of type " + serviceType, e);
141                        Collections.sort(services, SERVICE_COMPARATOR);
142                        return services;
143                }
144        }
145
146        @Override
147        public int compareTo(ServiceProvider o) {
148                return Integer.compare(getPriority(), o.getPriority());
149        }
150
151        @Override
152        public SystemOfUnitsService getSystemOfUnitsService() {
153                return getService(SystemOfUnitsService.class);
154        }
155
156        @Override
157        public UnitFormatService getUnitFormatService() {
158                return getService(UnitFormatService.class);
159        }
160        
161        @Override
162        public FormatService getFormatService() {
163                return getService(FormatService.class);
164        }
165
166         /**
167           * Return a factory for this quantity
168           * 
169           * @param quantity
170           *          the quantity type
171           * @return the {@link QuantityFactory}
172           * @throws NullPointerException
173           */
174          @Override
175          @SuppressWarnings("unchecked")
176          public final <Q extends Quantity<Q>> QuantityFactory<Q> getQuantityFactory(Class<Q> quantity) {
177            if (quantity == null)
178              throw new NullPointerException();
179            if (!QUANTITY_FACTORIES.containsKey(quantity)) {
180              synchronized (QUANTITY_FACTORIES) {
181                QUANTITY_FACTORIES.put(quantity, DefaultQuantityFactory.getInstance(quantity));
182                //QUANTITY_FACTORIES.put(quantity, ProxyQuantityFactory.getInstance(quantity)); FIXME this currently fails because some Quantity methods are not implemented by the proxy
183              }
184            }
185            return QUANTITY_FACTORIES.get(quantity);
186          }
187}