001/*
002 * Units of Measurement API
003 * Copyright (c) 2014-2023, 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 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 javax.measure.spi;
031
032import java.util.Collection;
033import java.util.EnumSet;
034import java.util.Set;
035import javax.measure.Prefix;
036
037/**
038 * This interface represents the service to obtain a {@link SystemOfUnits system
039 * of units}.
040 *
041 * <p>
042 * Common systems of units are "SI" (System International) or Metric system,
043 * "Imperial" (British), or "US" (US Customary).
044 * </p>
045 *
046 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
047 * @author <a href="mailto:werner@uom.technology">Werner Keil</a>
048 * @author <a href="mailto:martin.desruisseaux@geomatys.com">Martin
049 *         Desruisseaux</a>
050 * @version 1.8, April 3, 2023
051 * @since 1.0
052 *
053 * @see <a href=
054 *      "https://en.wikipedia.org/wiki/International_System_of_Units">Wikipedia:
055 *      International System of Units</a>
056 */
057public interface SystemOfUnitsService {
058
059    /**
060     * Returns the default {@link SystemOfUnits system of units}. Depending on the
061     * implementation this may be the <a href="https://en.wikipedia.org/wiki/International_System_of_Units">International
062     * System of Units</a> or another default system.
063     *
064     * @return the default system of units.
065     */
066    SystemOfUnits getSystemOfUnits();
067
068    /**
069     * Returns the system of units having the specified name or {@code null} if
070     * none is found.
071     *
072     * @param name the system of unit name.
073     * @return the system of units for the given name.
074     */
075    SystemOfUnits getSystemOfUnits(String name);
076
077    /**
078     * Gets a list with available systems for this {@link SystemOfUnitsService}.
079     *
080     * @return list of available systems of units, never null.
081     */
082    Collection<SystemOfUnits> getAvailableSystemsOfUnits();
083
084    /**
085     * Returns a {@link Set} containing the values of a particular {@link Prefix}
086     * type.
087     *
088     * <p>
089     * This method may be used to iterate over certain prefixes as follows:
090     * </p>
091     * <pre>{@code
092     *    for(MetricPrefix mp : service.getPrefixes(MetricPrefix.class))
093     *        System.out.println(p);
094     * }</pre>
095     *
096     * The default implementation assumes that prefixes of the given type are implemented as an enumeration.
097     * This is the case of the two default prefix implementations provided in JSR 385,
098     * namely {@link javax.measure.MetricPrefix} and {@link javax.measure.BinaryPrefix}.
099     * Implementors shall override this method if they provide prefixes implemented in a different way.
100     *
101     * @param <P> compile-time value of the {@code prefixType} argument
102     * @param prefixType the {@link Prefix} type
103     * @return a set containing the constant values of this Prefix type, in the
104     *         order they're declared
105     * @throws ClassCastException if the class is not compatible with the desired
106     *                            Prefix implementation or does not implement Prefix at all.
107     * @since 2.0
108     */
109    @SuppressWarnings({"unchecked", "rawtypes"})
110    default <P extends Prefix> Set<P> getPrefixes(Class<P> prefixType) {
111        // Following check is redundant with parameterized type but nevertheless applied as a safety.
112        if (Prefix.class.isAssignableFrom(prefixType)) {
113            EnumSet<? extends Enum<?>> prefixes = EnumSet.allOf(prefixType.asSubclass(Enum.class));
114            /*
115             * Following unchecked cast is safe for read operations because the given class implements
116             * 'prefixType' in addition of being an enumeration.  It is also safe for write operations
117             * because all enumerations are closed universes (so users can not add an instance unknown
118             * to EnumSet) and we would have got an exception before this point if 'prefixType' was not
119             * an enumeration in the sense of Class.isEnum().
120             */
121            return (EnumSet) prefixes;
122        } else {
123            throw new ClassCastException(String.format("%s does not implement Prefix", prefixType));
124            // TODO or should we throw a different exception here, MeasurementException or IllegalArgumentException?
125        }
126    }
127}