001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2020, Units of Measurement project.
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:werner@units.tech">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                public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, String name, String symbol) {
148                        return addUnit(units, unit, name, symbol, NAME_AND_SYMBOL);
149                }
150
151                /**
152                 * Adds a new named unit to the collection.
153                 * 
154                 * @param unit
155                 *            the unit being added.
156                 * @param name
157                 *            the name of the unit.
158                 * @param name
159                 *            the symbol of the unit.
160                 * @param style
161                 *            style of the unit.
162                 * @return <code>unit</code>.
163                 * @since 1.0.1
164                 */
165                @SuppressWarnings("unchecked")
166                public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, final String name, final String symbol,
167                                UnitStyle style) {
168                        switch (style) {
169                        case NAME:
170                                if (name != null && unit instanceof AbstractUnit) {
171                                        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
172                                        aUnit.setName(name);
173                                        units.add(aUnit);
174                                        return (U) aUnit;
175                                }
176                                break;
177                        case NAME_AND_SYMBOL:
178                        case SYMBOL:
179                                if (unit instanceof AbstractUnit) {
180                                        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
181                                        if (name != null && NAME_AND_SYMBOL.equals(style)) { 
182                                                aUnit.setName(name);
183                                        }
184                                        if (name != null && (SYMBOL.equals(style) || NAME_AND_SYMBOL.equals(style))) {
185                                                aUnit.setSymbol(symbol);
186                                        }
187                                        units.add(aUnit);
188                                        return (U) aUnit;
189                                }
190                                break;
191                        case SYMBOL_AND_LABEL:
192                                if (name != null && symbol != null && unit instanceof AbstractUnit) {
193                                        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
194                                        aUnit.setName(name);
195                                        if (SYMBOL.equals(style) || SYMBOL_AND_LABEL.equals(style)) {
196                                                aUnit.setSymbol(symbol);
197                                        }
198                                        if (LABEL.equals(style) || SYMBOL_AND_LABEL.equals(style)) {
199                                                SimpleUnitFormat.getInstance().label(unit, symbol);
200                                        }
201                                        units.add(aUnit);
202                                        return (U) aUnit;
203                                }
204                                break;
205                        default:
206                                if (logger.isLoggable(Level.FINEST)) {
207                                        logger.log(Level.FINEST,
208                                                        "Unknown style " + style + "; unit " + unit + " can't be rendered with '" + symbol + "'.");
209                                }
210                                break;
211                        }
212                        if (LABEL.equals(style) || SYMBOL_AND_LABEL.equals(style)) {
213                                SimpleUnitFormat.getInstance().label(unit, symbol);
214                        }
215                        units.add(unit);
216                        return unit;
217                }
218
219                /**
220                 * Adds a new labeled unit to the set.
221                 * 
222                 * @param units
223                 *            the set to add to.
224                 * 
225                 * @param unit
226                 *            the unit being added.
227                 * @param text
228                 *            the text for the unit.
229                 * @param style
230                 *            style of the unit.
231                 * @return <code>unit</code>.
232                 * @since 1.0.1
233                 */
234                @SuppressWarnings("unchecked")
235                public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, String text, UnitStyle style) {
236                        switch (style) {
237                        case NAME:
238                                if (text != null && unit instanceof AbstractUnit) {
239                                        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
240                                        aUnit.setName(text);
241                                        units.add(aUnit);
242                                        return (U) aUnit;
243                                }
244                                break;
245                        case SYMBOL:
246                                if (text != null && unit instanceof AbstractUnit) {
247                                        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
248                                        aUnit.setSymbol(text);
249                                        units.add(aUnit);
250                                        return (U) aUnit;
251                                }
252                                break;
253                        case SYMBOL_AND_LABEL:
254                                if (text != null && unit instanceof AbstractUnit) {
255                                        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
256                                        aUnit.setSymbol(text);
257                                        units.add(aUnit);
258                                        SimpleUnitFormat.getInstance().label(aUnit, text);
259                                        return (U) aUnit;
260                                } 
261                                // label in any case, returning below
262                                SimpleUnitFormat.getInstance().label(unit, text);
263                                break;
264                        case LABEL:
265                                SimpleUnitFormat.getInstance().label(unit, text);
266                                break;
267                        default:
268                                logger.log(Level.FINEST,
269                                                "Unknown style " + style + "; unit " + unit + " can't be rendered with '" + text + "'.");
270                                break;
271                        }
272                        units.add(unit);
273                        return unit;
274                }
275        }
276}