001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2025, 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.unit; 031 032import javax.measure.Dimension; 033import javax.measure.Quantity; 034import javax.measure.Unit; 035import javax.measure.spi.ServiceProvider; 036import javax.measure.spi.SystemOfUnits; 037 038import tech.units.indriya.AbstractUnit; 039import tech.units.indriya.spi.DefaultServiceProvider; 040 041import java.io.Serializable; 042import java.util.HashMap; 043import java.util.List; 044import java.util.Map; 045import java.util.Objects; 046import java.util.logging.Level; 047import java.util.logging.Logger; 048 049/** 050 * <p> 051 * This class represents a dimension of a unit of measurement. 052 * </p> 053 * 054 * <p> 055 * The dimension associated to any given quantity are given by the published 056 * {@link Dimension} instances. For convenience, a static method 057 * <code>UnitDimension.of(Class)</code> aggregating the results of all 058 * 059 * {@link Dimension} instances is provided.<br> 060 * <br> 061 * <code> 062 * Dimension speedDimension 063 * = UnitDimension.of(Speed.class); 064 * </code> 065 * </p> 066 * 067 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 068 * @author <a href="mailto:werner@units.tech">Werner Keil</a> 069 * @author Martin Desruisseaux (Geomatys) 070 * @author Andi Huber 071 * @version 2.2, $Date: 2025-02-19 $ 072 * @since 2.0 073 */ 074public class UnitDimension implements Dimension, Serializable { 075 /** */ 076 private static final long serialVersionUID = 7806787530512644696L; 077 078 private static final Logger LOGGER = Logger.getLogger(UnitDimension.class.getName()); 079 080 /** 081 * Holds dimensionless. 082 * 083 * @since 1.0 084 */ 085 public static final Dimension NONE = new UnitDimension(AbstractUnit.ONE); 086 087 /** 088 * Holds length dimension (L). 089 * 090 * @since 1.0 091 */ 092 public static final Dimension LENGTH = new UnitDimension('L'); 093 094 /** 095 * Holds mass dimension (M). 096 * 097 * @since 1.0 098 */ 099 public static final Dimension MASS = new UnitDimension('M'); 100 101 /** 102 * Holds time dimension (T). 103 * 104 * @since 1.0 105 */ 106 public static final Dimension TIME = new UnitDimension('T'); 107 108 /** 109 * Holds electric current dimension (I). 110 * 111 * @since 1.0 112 */ 113 public static final Dimension ELECTRIC_CURRENT = new UnitDimension('I'); 114 115 /** 116 * Holds temperature dimension (Θ). 117 * 118 * @since 1.0 119 */ 120 public static final Dimension TEMPERATURE = new UnitDimension('\u0398'); 121 122 /** 123 * Holds amount of substance dimension (N). 124 * 125 * @since 1.0 126 */ 127 public static final Dimension AMOUNT_OF_SUBSTANCE = new UnitDimension('N'); 128 129 /** 130 * Holds luminous intensity dimension (J). 131 */ 132 public static final Dimension LUMINOUS_INTENSITY = new UnitDimension('J'); 133 134 /** 135 * Holds the pseudo unit associated to this dimension. 136 */ 137 private final Unit<?> pseudoUnit; 138 139 /** 140 * Returns the dimension for the specified quantity type by aggregating the 141 * results from the registered {@link javax.measure.spi.SystemOfUnits SystemsOfUnits} or <code>null</code> if the specified 142 * quantity is unknown. 143 * 144 * @param quantityType the quantity type. 145 * @return the dimension for the quantity type or <code>null</code>. 146 * @since 1.1 147 * @see javax.measure.spi.SystemOfUnits#getUnit(Class) 148 */ 149 public static <Q extends Quantity<Q>> Dimension of(Class<Q> quantityType) { 150 Unit<Q> typedUnit = typedUnitFor(quantityType); 151 if (typedUnit == null && LOGGER.isLoggable(Level.FINE)) { 152 LOGGER.log(Level.FINE, "Quantity type: " + quantityType + " unknown"); 153 } 154 return (typedUnit != null) ? typedUnit.getDimension() : null; 155 } 156 157 /** 158 * Returns the typed unit for the specified quantity type by aggregating the 159 * results from the registered {@link javax.measure.spi.SystemOfUnits SystemsOfUnits} or <code>null</code> if the specified 160 * quantity is unknown. 161 * 162 * @param quantityType the quantity type. 163 * @return the unit for the quantity type or <code>null</code>. 164 * @since 2.2 165 * @see javax.measure.spi.SystemOfUnits#getUnit(Class) 166 */ 167 private static <Q extends Quantity<Q>> Unit<Q> typedUnitFor(Class<Q> quantityType) { 168 List<ServiceProvider> providers = DefaultServiceProvider.available(); 169 for (ServiceProvider provider: providers) { 170 for (SystemOfUnits systemOfUnits : provider.getSystemOfUnitsService().getAvailableSystemsOfUnits()) { 171 Unit<Q> result = systemOfUnits.getUnit(quantityType); 172 if (result != null) return result; 173 } 174 } 175 return null; 176 } 177 178 /** 179 * Returns the dimension for the specified symbol. 180 * 181 * @param sambol the quantity symbol. 182 * @return the dimension for the given symbol. 183 * @since 1.0.1 184 */ 185 public static Dimension parse(char symbol) { 186 return new UnitDimension(symbol); 187 } 188 189 /** 190 * Returns the unit dimension having the specified symbol. 191 * 192 * @param symbol the associated symbol. 193 */ 194 @SuppressWarnings("rawtypes") 195 private UnitDimension(char symbol) { 196 pseudoUnit = new BaseUnit("[" + symbol + ']', NONE); 197 } 198 199 /** 200 * Constructor from pseudo-unit (not visible). 201 * 202 * @param pseudoUnit the pseudo-unit. 203 */ 204 private UnitDimension(Unit<?> pseudoUnit) { 205 this.pseudoUnit = pseudoUnit; 206 } 207 208 /** 209 * Default Constructor (not visible). 210 * 211 */ 212 protected UnitDimension() { 213 this(AbstractUnit.ONE); 214 } 215 216 217 /** 218 * Returns the product of this dimension with the one specified. 219 * If the specified dimension is not a <code>UnitDimension</code>, then 220 * <code>that.multiply(this)</code> is returned. 221 * 222 * @param that the dimension multiplicand. 223 * @return <code>this * that</code> 224 * @since 1.0 225 */ 226 public Dimension multiply(Dimension that) { 227 return that instanceof UnitDimension 228 ? this.multiply((UnitDimension) that) 229 : that.multiply(this); 230 } 231 232 /** 233 * Returns the product of this dimension with the one specified. 234 * 235 * @param that the dimension multiplicand. 236 * @return <code>this * that</code> 237 * @since 1.0 238 */ 239 private UnitDimension multiply(UnitDimension that) { 240 return new UnitDimension(this.pseudoUnit.multiply(that.pseudoUnit)); 241 } 242 243 /** 244 * Returns the quotient of this dimension with the one specified. 245 * If the specified dimension is not a <code>UnitDimension</code>, then 246 * <code>that.divide(this).pow(-1)</code> is returned. 247 * 248 * @param that the dimension divisor. 249 * @return <code>this / that</code> 250 * @since 1.0 251 */ 252 public Dimension divide(Dimension that) { 253 return that instanceof UnitDimension 254 ? this.divide((UnitDimension) that) 255 : that.divide(this).pow(-1); 256 } 257 258 /** 259 * Returns the quotient of this dimension with the one specified. 260 * 261 * @param that the dimension divisor. 262 * @return <code>this / that</code> 263 * @since 1.0 264 */ 265 private UnitDimension divide(UnitDimension that) { 266 return new UnitDimension(ProductUnit.ofQuotient(pseudoUnit, that.pseudoUnit)); 267 } 268 269 /** 270 * Returns this dimension raised to an exponent. 271 * 272 * @param n the exponent. 273 * @return the result of raising this dimension to the exponent. 274 * @since 1.0 275 */ 276 public UnitDimension pow(int n) { 277 return new UnitDimension(this.pseudoUnit.pow(n)); 278 } 279 280 /** 281 * Returns the given root of this dimension. 282 * 283 * @param n the root's order. 284 * @return the result of taking the given root of this dimension. 285 * @throws ArithmeticException if <code>n == 0</code>. 286 * @since 1.0 287 */ 288 public UnitDimension root(int n) { 289 return new UnitDimension(this.pseudoUnit.root(n)); 290 } 291 292 /** 293 * Returns the fundamental (base) dimensions and their exponent whose product is 294 * this dimension or <code>null</code> if this dimension is a fundamental 295 * dimension. 296 * 297 * @return the mapping between the base dimensions and their exponent. 298 * @since 1.0 299 */ 300 @SuppressWarnings("rawtypes") 301 public Map<? extends Dimension, Integer> getBaseDimensions() { 302 Map<? extends Unit, Integer> pseudoUnits = pseudoUnit.getBaseUnits(); 303 if (pseudoUnits == null) { 304 return null; 305 } 306 final Map<UnitDimension, Integer> baseDimensions = new HashMap<>(); 307 for (Map.Entry<? extends Unit, Integer> entry : pseudoUnits.entrySet()) { 308 baseDimensions.put(new UnitDimension(entry.getKey()), entry.getValue()); 309 } 310 return baseDimensions; 311 } 312 313 @Override 314 public String toString() { 315 return pseudoUnit.toString(); 316 } 317 318 @Override 319 public boolean equals(Object obj) { 320 if (this == obj) { 321 return true; 322 } 323 if (obj instanceof UnitDimension) { 324 UnitDimension other = (UnitDimension) obj; 325 return Objects.equals(pseudoUnit, other.pseudoUnit); 326 } 327 return false; 328 } 329 330 @Override 331 public int hashCode() { 332 return Objects.hashCode(pseudoUnit); 333 } 334}