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.1, April 1, 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  protected static class Helper {
104    static Set<Unit<?>> getUnitsOfDimension(final Set<Unit<?>> units, Dimension dimension) {
105            if (dimension != null) {
106                return units.stream().filter(u -> dimension.equals(u.getDimension())).collect(Collectors.toSet());
107
108            }
109            return null;
110        }
111
112    /**
113     * Adds a new named unit to the collection.
114     * 
115     * @param unit
116     *          the unit being added.
117     * @param name
118     *          the name of the unit.
119     * @return <code>unit</code>.
120     * @since 1.0
121     */
122    public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, String name) {
123      return addUnit(units, unit, name, NAME);
124    }
125
126    /**
127     * Adds a new named unit to the collection.
128     * 
129     * @param unit
130     *          the unit being added.
131     * @param name
132     *          the name of the unit.
133     * @param name
134     *          the symbol of the unit.
135     * @return <code>unit</code>.
136     * @since 1.0
137     */
138    @SuppressWarnings("unchecked")
139    public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, String name, String symbol) {
140      if (name != null && symbol != null && unit instanceof AbstractUnit) {
141        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
142        aUnit.setName(name);
143        aUnit.setSymbol(symbol);
144        units.add(aUnit);
145        return (U) aUnit;
146      }
147      if (name != null && unit instanceof AbstractUnit) {
148        AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
149        aUnit.setName(name);
150        units.add(aUnit);
151        return (U) aUnit;
152      }
153      units.add(unit);
154      return unit;
155    }
156
157    /**
158     * Adds a new named unit to the collection.
159     * 
160     * @param unit
161     *          the unit being added.
162     * @param name
163     *          the name of the unit.
164     * @param name
165     *          the symbol of the unit.
166     * @param style
167     *          style of the unit.
168     * @return <code>unit</code>.
169     * @since 1.0.1
170     */
171    @SuppressWarnings("unchecked")
172    public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, final String name, final String symbol, UnitStyle style) {
173      switch (style) {
174        case NAME:
175        case SYMBOL:
176        case SYMBOL_AND_LABEL:
177          if (name != null && symbol != null && unit instanceof AbstractUnit) {
178            AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
179            aUnit.setName(name);
180            if (SYMBOL.equals(style) || SYMBOL_AND_LABEL.equals(style)) {
181              aUnit.setSymbol(symbol);
182            }
183            if (LABEL.equals(style) || SYMBOL_AND_LABEL.equals(style)) {
184              SimpleUnitFormat.getInstance().label(unit, symbol);
185            }
186            units.add(aUnit);
187            return (U) aUnit;
188          }
189          if (name != null && unit instanceof AbstractUnit) {
190            AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
191            aUnit.setName(name);
192            units.add(aUnit);
193            return (U) aUnit;
194          }
195          break;
196        default:
197          if (logger.isLoggable(Level.FINEST)) {
198            logger.log(Level.FINEST, "Unknown style " + style + "; unit " + unit + " can't be rendered with '" + symbol + "'.");
199          }
200          break;
201      }
202      if (LABEL.equals(style) || SYMBOL_AND_LABEL.equals(style)) {
203        SimpleUnitFormat.getInstance().label(unit, symbol);
204      }
205      units.add(unit);
206      return unit;
207    }
208
209    /**
210     * Adds a new labeled unit to the set.
211     * 
212     * @param units
213     *          the set to add to.
214     * 
215     * @param unit
216     *          the unit being added.
217     * @param text
218     *          the text for the unit.
219     * @param style
220     *          style of the unit.
221     * @return <code>unit</code>.
222     * @since 1.0.1
223     */
224    @SuppressWarnings("unchecked")
225    public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, String text, UnitStyle style) {
226      switch (style) {
227        case NAME:
228          if (text != null && unit instanceof AbstractUnit) {
229            AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
230            aUnit.setName(text);
231            units.add(aUnit);
232            return (U) aUnit;
233          }
234          break;
235        case SYMBOL:
236          if (text != null && unit instanceof AbstractUnit) {
237            AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
238            aUnit.setSymbol(text);
239            units.add(aUnit);
240            return (U) aUnit;
241          }
242          break;
243        case SYMBOL_AND_LABEL:
244          if (text != null && unit instanceof AbstractUnit) {
245            AbstractUnit<?> aUnit = (AbstractUnit<?>) unit;
246            aUnit.setSymbol(text);
247            units.add(aUnit);
248            SimpleUnitFormat.getInstance().label(aUnit, text);
249            return (U) aUnit;
250          } else { // label in any case, returning below
251            SimpleUnitFormat.getInstance().label(unit, text);
252          }
253          break;
254        case LABEL:
255          SimpleUnitFormat.getInstance().label(unit, text);
256          break;
257        default:
258          logger.log(Level.FINEST, "Unknown style " + style + "; unit " + unit + " can't be rendered with '" + text + "'.");
259          break;
260      }
261      units.add(unit);
262      return unit;
263    }
264  }
265}