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;
031
032import javax.measure.Dimension;
033import javax.measure.Quantity;
034import javax.measure.Unit;
035import javax.measure.spi.SystemOfUnits;
036
037import tech.units.indriya.format.SimpleUnitFormat;
038import tech.units.indriya.format.UnitStyle;
039import tech.uom.lib.common.function.Nameable;
040
041import static tech.units.indriya.format.UnitStyle.*;
042
043import java.util.*;
044import java.util.logging.Level;
045import java.util.logging.Logger;
046import java.util.stream.Collectors;
047
048/**
049 * <p>
050 * An abstract base class for unit systems.
051 * </p>
052 *
053 * @author <a href="mailto:units@catmedia.us">Werner Keil</a>
054 * @version 1.2, June 21, 2018
055 * @since 1.0
056 */
057public abstract class AbstractSystemOfUnits implements SystemOfUnits, Nameable {
058        /**
059         * Holds the units.
060         */
061        protected final Set<Unit<?>> units = new HashSet<>();
062
063        /**
064         * Holds the mapping quantity to unit.
065         */
066        @SuppressWarnings("rawtypes")
067        protected final Map<Class<? extends Quantity>, Unit> quantityToUnit = new HashMap<>();
068
069        protected static final Logger logger = Logger.getLogger(AbstractSystemOfUnits.class.getName());
070
071        /**
072         * The natural logarithm.
073         **/
074        protected static final double E = 2.71828182845904523536028747135266;
075
076        /*
077         * (non-Javadoc)
078         * 
079         * @see SystemOfUnits#getName()
080         */
081        public abstract String getName();
082
083        // ///////////////////
084        // Collection View //
085        // ///////////////////
086        @Override
087        public Set<Unit<?>> getUnits() {
088                return Collections.unmodifiableSet(units);
089        }
090
091        @Override
092        public Set<? extends Unit<?>> getUnits(Dimension dimension) {
093                return this.getUnits().stream().filter(unit -> dimension.equals(unit.getDimension()))
094                                .collect(Collectors.toSet());
095        }
096
097        @SuppressWarnings("unchecked")
098        @Override
099        public <Q extends Quantity<Q>> Unit<Q> getUnit(Class<Q> quantityType) {
100                return quantityToUnit.get(quantityType);
101        }
102
103        @Override
104        public Unit<?> getUnit(String string) {
105                Objects.requireNonNull(string);
106                return this.getUnits().stream()
107                      .filter((u) -> string.equals(u.toString()))
108                      .findAny()
109                      .orElse(null);
110        }
111        
112        protected static class Helper {
113                static Set<Unit<?>> getUnitsOfDimension(final Set<Unit<?>> units, Dimension dimension) {
114                        if (dimension != null) {
115                                return units.stream().filter(u -> dimension.equals(u.getDimension())).collect(Collectors.toSet());
116
117                        }
118                        return null;
119                }
120
121                /**
122                 * Adds a new named unit to the collection.
123                 * 
124                 * @param unit
125                 *            the unit being added.
126                 * @param name
127                 *            the name of the unit.
128                 * @return <code>unit</code>.
129                 * @since 1.0
130                 */
131                public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, String name) {
132                        return addUnit(units, unit, name, NAME);
133                }
134
135                /**
136                 * Adds a new named unit to the collection.
137                 * 
138                 * @param unit
139                 *            the unit being added.
140                 * @param name
141                 *            the name of the unit.
142                 * @param name
143                 *            the symbol of the unit.
144                 * @return <code>unit</code>.
145                 * @since 1.0
146                 */
147                @SuppressWarnings("unchecked")
148                public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, String name, String symbol) {
149                        if (name != null && symbol != null && unit instanceof AbstractUnit) {
150                                AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
151                                aUnit.setName(name);
152                                aUnit.setSymbol(symbol);
153                                units.add(aUnit);
154                                return (U) aUnit;
155                        }
156                        if (name != null && unit instanceof AbstractUnit) {
157                                AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
158                                aUnit.setName(name);
159                                units.add(aUnit);
160                                return (U) aUnit;
161                        }
162                        units.add(unit);
163                        return unit;
164                }
165
166                /**
167                 * Adds a new named unit to the collection.
168                 * 
169                 * @param unit
170                 *            the unit being added.
171                 * @param name
172                 *            the name of the unit.
173                 * @param name
174                 *            the symbol of the unit.
175                 * @param style
176                 *            style of the unit.
177                 * @return <code>unit</code>.
178                 * @since 1.0.1
179                 */
180                @SuppressWarnings("unchecked")
181                public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, final String name, final String symbol,
182                                UnitStyle style) {
183                        switch (style) {
184                        case NAME:
185                        case SYMBOL:
186                        case SYMBOL_AND_LABEL:
187                                if (name != null && symbol != null && unit instanceof AbstractUnit) {
188                                        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
189                                        aUnit.setName(name);
190                                        if (SYMBOL.equals(style) || SYMBOL_AND_LABEL.equals(style)) {
191                                                aUnit.setSymbol(symbol);
192                                        }
193                                        if (LABEL.equals(style) || SYMBOL_AND_LABEL.equals(style)) {
194                                                SimpleUnitFormat.getInstance().label(unit, symbol);
195                                        }
196                                        units.add(aUnit);
197                                        return (U) aUnit;
198                                }
199                                if (name != null && unit instanceof AbstractUnit) {
200                                        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
201                                        aUnit.setName(name);
202                                        units.add(aUnit);
203                                        return (U) aUnit;
204                                }
205                                break;
206                        default:
207                                if (logger.isLoggable(Level.FINEST)) {
208                                        logger.log(Level.FINEST,
209                                                        "Unknown style " + style + "; unit " + unit + " can't be rendered with '" + symbol + "'.");
210                                }
211                                break;
212                        }
213                        if (LABEL.equals(style) || SYMBOL_AND_LABEL.equals(style)) {
214                                SimpleUnitFormat.getInstance().label(unit, symbol);
215                        }
216                        units.add(unit);
217                        return unit;
218                }
219
220                /**
221                 * Adds a new labeled unit to the set.
222                 * 
223                 * @param units
224                 *            the set to add to.
225                 * 
226                 * @param unit
227                 *            the unit being added.
228                 * @param text
229                 *            the text for the unit.
230                 * @param style
231                 *            style of the unit.
232                 * @return <code>unit</code>.
233                 * @since 1.0.1
234                 */
235                @SuppressWarnings("unchecked")
236                public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, String text, UnitStyle style) {
237                        switch (style) {
238                        case NAME:
239                                if (text != null && unit instanceof AbstractUnit) {
240                                        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
241                                        aUnit.setName(text);
242                                        units.add(aUnit);
243                                        return (U) aUnit;
244                                }
245                                break;
246                        case SYMBOL:
247                                if (text != null && unit instanceof AbstractUnit) {
248                                        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
249                                        aUnit.setSymbol(text);
250                                        units.add(aUnit);
251                                        return (U) aUnit;
252                                }
253                                break;
254                        case SYMBOL_AND_LABEL:
255                                if (text != null && unit instanceof AbstractUnit) {
256                                        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
257                                        aUnit.setSymbol(text);
258                                        units.add(aUnit);
259                                        SimpleUnitFormat.getInstance().label(aUnit, text);
260                                        return (U) aUnit;
261                                } else { // label in any case, returning below
262                                        SimpleUnitFormat.getInstance().label(unit, text);
263                                }
264                                break;
265                        case LABEL:
266                                SimpleUnitFormat.getInstance().label(unit, text);
267                                break;
268                        default:
269                                logger.log(Level.FINEST,
270                                                "Unknown style " + style + "; unit " + unit + " can't be rendered with '" + text + "'.");
271                                break;
272                        }
273                        units.add(unit);
274                        return unit;
275                }
276        }
277}