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