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