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