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