001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-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, 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.1, Mar 28, 2021 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() 113 .filter((u) -> string.equals(u.toString())) 114 .findAny() 115 .orElse(null); 116 } 117 118 /** 119 * <p> 120 * Returns a unit with the given {@linkplain String string} representation in a 121 * particular {@linkplain UnitStyle style} or {@code null} if none is found in 122 * this unit system and requested style. 123 * </p> 124 * <p> 125 * <b>NOTE:</b> Use {@code ignoreCase} carefully, as it will find the 126 * <b>FIRST</b> unit for a particular string, e.g. the symbol of {@code SECOND} 127 * and {@code SIEMENS} would be the same without case, but the UPPERCASE letter 128 * sorted first. 129 * </p> 130 * 131 * @param string the string representation of a unit, not {@code null}. 132 * @param style the style of unit representation. 133 * @param ignoreCase ignore the case or not? 134 * @return the unit with the given string representation. 135 * @since 2.0 136 */ 137 public Unit<?> getUnit(String string, UnitStyle style, boolean ignoreCase) { 138 Objects.requireNonNull(string); 139 switch (style) { 140 case NAME: 141 if (ignoreCase) { 142 return this.getUnits().stream().filter((u) -> string.equalsIgnoreCase(u.getName())).findFirst() 143 .orElse(null); 144 } else { 145 return this.getUnits().stream().filter((u) -> string.equals(u.getName())).findFirst().orElse(null); 146 } 147 case SYMBOL: 148 if (ignoreCase) { 149 return this.getUnits().stream().filter((u) -> string.equalsIgnoreCase(u.getSymbol())).findFirst() 150 .orElse(null); 151 } else { 152 return this.getUnits().stream().filter((u) -> string.equals(u.getSymbol())).findFirst().orElse(null); 153 } 154 default: 155 return getUnit(string); 156 } 157 } 158 159 /** 160 * Returns a unit with the given {@linkplain String string} representation in a 161 * particular {@linkplain UnitStyle style} or {@code null} if none is found in 162 * this unit system and requested style. 163 * 164 * @param string the string representation of a unit, not {@code null}. 165 * @param style the style of unit representation. 166 * @return the unit with the given string representation. 167 * @since 2.0 168 */ 169 public Unit<?> getUnit(String string, UnitStyle style) { 170 return getUnit(string, style, false); 171 } 172 173 protected static class Helper { 174 static Set<Unit<?>> getUnitsOfDimension(final Set<Unit<?>> units, Dimension dimension) { 175 if (dimension != null) { 176 return units.stream().filter(u -> dimension.equals(u.getDimension())).collect(Collectors.toSet()); 177 178 } 179 return null; 180 } 181 182 /** 183 * Adds a new named unit to the collection. 184 * 185 * @param unit the unit being added. 186 * @param name the name of the unit. 187 * @return <code>unit</code>. 188 * @since 1.0 189 */ 190 public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, String name) { 191 return addUnit(units, unit, name, NAME); 192 } 193 194 /** 195 * Adds a new named unit to the collection. 196 * 197 * @param unit the unit being added. 198 * @param name the name of the unit. 199 * @param name the symbol of the unit. 200 * @return <code>unit</code>. 201 * @since 1.0 202 */ 203 public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, String name, String symbol) { 204 return addUnit(units, unit, name, symbol, NAME_AND_SYMBOL); 205 } 206 207 /** 208 * Adds a new named unit to the collection. 209 * 210 * @param unit the unit being added. 211 * @param name the name of the unit. 212 * @param name the symbol of the unit. 213 * @param style style of the unit. 214 * @return <code>unit</code>. 215 * @since 1.0.1 216 */ 217 @SuppressWarnings("unchecked") 218 public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, final String name, final String symbol, 219 UnitStyle style) { 220 switch (style) { 221 case NAME: 222 if (name != null && unit instanceof AbstractUnit) { 223 AbstractUnit<?> aUnit = (AbstractUnit<?>) unit; 224 aUnit.setName(name); 225 units.add(aUnit); 226 return (U) aUnit; 227 } 228 break; 229 case NAME_AND_SYMBOL: 230 case SYMBOL: 231 if (unit instanceof AbstractUnit) { 232 AbstractUnit<?> aUnit = (AbstractUnit<?>) unit; 233 if (name != null && NAME_AND_SYMBOL.equals(style)) { 234 aUnit.setName(name); 235 } 236 if (name != null && (SYMBOL.equals(style) || NAME_AND_SYMBOL.equals(style))) { 237 aUnit.setSymbol(symbol); 238 } 239 units.add(aUnit); 240 return (U) aUnit; 241 } 242 break; 243 case SYMBOL_AND_LABEL: 244 if (name != null && symbol != null && unit instanceof AbstractUnit) { 245 AbstractUnit<?> aUnit = (AbstractUnit<?>) unit; 246 aUnit.setName(name); 247 if (SYMBOL.equals(style) || SYMBOL_AND_LABEL.equals(style)) { 248 aUnit.setSymbol(symbol); 249 } 250 if (LABEL.equals(style) || SYMBOL_AND_LABEL.equals(style)) { 251 SimpleUnitFormat.getInstance().label(unit, symbol); 252 } 253 units.add(aUnit); 254 return (U) aUnit; 255 } 256 break; 257 default: 258 if (logger.isLoggable(Level.FINEST)) { 259 logger.log(Level.FINEST, 260 "Unknown style " + style + "; unit " + unit + " can't be rendered with '" + symbol + "'."); 261 } 262 break; 263 } 264 if (LABEL.equals(style) || SYMBOL_AND_LABEL.equals(style)) { 265 SimpleUnitFormat.getInstance().label(unit, symbol); 266 } 267 units.add(unit); 268 return unit; 269 } 270 271 /** 272 * Adds a new labeled unit to the set. 273 * 274 * @param units the set to add to. 275 * @param unit the unit being added. 276 * @param text the text for the unit. 277 * @param style style of the unit. 278 * @return <code>unit</code>. 279 * @since 1.0.1 280 */ 281 @SuppressWarnings("unchecked") 282 public static <U extends Unit<?>> U addUnit(Set<Unit<?>> units, U unit, String text, UnitStyle style) { 283 switch (style) { 284 case NAME: 285 if (text != null && unit instanceof AbstractUnit) { 286 AbstractUnit<?> aUnit = (AbstractUnit<?>) unit; 287 aUnit.setName(text); 288 units.add(aUnit); 289 return (U) aUnit; 290 } 291 break; 292 case SYMBOL: 293 if (text != null && unit instanceof AbstractUnit) { 294 AbstractUnit<?> aUnit = (AbstractUnit<?>) unit; 295 aUnit.setSymbol(text); 296 units.add(aUnit); 297 return (U) aUnit; 298 } 299 break; 300 case SYMBOL_AND_LABEL: 301 if (text != null && unit instanceof AbstractUnit) { 302 AbstractUnit<?> aUnit = (AbstractUnit<?>) unit; 303 aUnit.setSymbol(text); 304 units.add(aUnit); 305 SimpleUnitFormat.getInstance().label(aUnit, text); 306 return (U) aUnit; 307 } 308 // label in any case, returning below 309 SimpleUnitFormat.getInstance().label(unit, text); 310 break; 311 case LABEL: 312 SimpleUnitFormat.getInstance().label(unit, text); 313 break; 314 default: 315 logger.log(Level.FINEST, 316 "Unknown style " + style + "; unit " + unit + " can't be rendered with '" + text + "'."); 317 break; 318 } 319 units.add(unit); 320 return unit; 321 } 322 } 323}