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