001/*
002 * Units of Measurement Implementation for Java SE
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.uom.se.spi;
031
032import javax.measure.Quantity;
033import javax.measure.spi.QuantityFactory;
034import javax.measure.spi.ServiceProvider;
035import javax.measure.spi.SystemOfUnitsService;
036import javax.measure.spi.UnitFormatService;
037
038import tec.uom.se.quantity.DefaultQuantityFactory;
039
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 0.9
057 */
058public class DefaultServiceProvider extends ServiceProvider implements Comparable<ServiceProvider> {
059        /**
060         * List of services loaded, per class.
061         */
062        @SuppressWarnings("rawtypes")
063        private final Map<Class, List<Object>> servicesLoaded = new ConcurrentHashMap<>();
064
065        private static final Comparator<Object> SERVICE_COMPARATOR = DefaultServiceProvider::compareServices;
066
067        @SuppressWarnings("rawtypes")
068        private final Map<Class, QuantityFactory> QUANTITY_FACTORIES = new ConcurrentHashMap<>();
069
070        /**
071         * Returns a priority value of 10.
072         *
073         * @return 10, overriding the default provider.
074         */
075        @Override
076        public int getPriority() {
077                return 10;
078        }
079
080        /**
081         * Loads and registers services.
082         *
083         * @param serviceType
084         *            The service type.
085         * @param <T>
086         *            the concrete type.
087         * @return the items found, never {@code null}.
088         */
089        protected <T> List<T> getServices(final Class<T> serviceType) {
090                @SuppressWarnings("unchecked")
091                List<T> found = (List<T>) servicesLoaded.get(serviceType);
092                if (found != null) {
093                        return found;
094                }
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        static int compareServices(Object o1, Object o2) {
108                int prio1 = 0;
109                int prio2 = 0;
110//              Priority prio1Annot = o1.getClass().getAnnotation(Priority.class);
111//              if (prio1Annot != null) {
112//                      prio1 = prio1Annot.value();
113//              }
114//              Priority prio2Annot = o2.getClass().getAnnotation(Priority.class);
115//              if (prio2Annot != null) {
116//                      prio2 = prio2Annot.value();
117//              }
118                if (prio1 < prio2) {
119                        return 1;
120                }
121                if (prio2 < prio1) {
122                        return -1;
123                }
124                return o2.getClass().getSimpleName().compareTo(o1.getClass().getSimpleName());
125        }
126
127        /**
128         * Loads and registers services.
129         *
130         * @param serviceType
131         *            The service type.
132         * @param <T>
133         *            the concrete type.
134         * @return the items found, never {@code null}.
135         */
136        private <T> List<T> loadServices(final Class<T> serviceType) {
137                List<T> services = new ArrayList<>();
138                try {
139                        for (T t : ServiceLoader.load(serviceType)) {
140                                services.add(t);
141                        }
142                        Collections.sort(services, SERVICE_COMPARATOR);
143                        @SuppressWarnings("unchecked")
144                        final List<T> previousServices = (List<T>) servicesLoaded.putIfAbsent(serviceType, (List<Object>) services);
145                        return Collections.unmodifiableList(previousServices != null ? previousServices : services);
146                } catch (Exception e) {
147                        Logger.getLogger(DefaultServiceProvider.class.getName()).log(Level.WARNING,
148                                        "Error loading services of type " + serviceType, e);
149                        Collections.sort(services, SERVICE_COMPARATOR);
150                        return services;
151                }
152        }
153
154        @Override
155        public int compareTo(ServiceProvider o) {
156                return Integer.compare(getPriority(), o.getPriority());
157        }
158
159        @Override
160        public SystemOfUnitsService getSystemOfUnitsService() {
161                return getService(SystemOfUnitsService.class);
162        }
163
164        @Override
165        public UnitFormatService getUnitFormatService() {
166                return getService(UnitFormatService.class);
167        }
168
169         /**
170           * Return a factory for this quantity
171           * 
172           * @param quantity
173           *          the quantity type
174           * @return the {@link QuantityFactory}
175           * @throws NullPointerException
176           */
177          @Override
178          @SuppressWarnings("unchecked")
179          public final <Q extends Quantity<Q>> QuantityFactory<Q> getQuantityFactory(Class<Q> quantity) {
180            if (quantity == null)
181              throw new NullPointerException();
182            if (!QUANTITY_FACTORIES.containsKey(quantity)) {
183              synchronized (QUANTITY_FACTORIES) {
184                QUANTITY_FACTORIES.put(quantity, DefaultQuantityFactory.getInstance(quantity));
185              }
186            }
187            return QUANTITY_FACTORIES.get(quantity);
188          }
189}