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