001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.units; 006 007import edu.wpi.first.units.measure.Mult; 008import java.util.Objects; 009 010/** 011 * A combinatory unit type that is equivalent to the product of two other others. Note that 012 * algebraic reduction is not possible in Java's generic type system, so {@code MultUnit<A, B>} is 013 * not type-compatible with {@code MultUnit<B, A>}! 014 * 015 * @param <A> the type of the first unit in the result 016 * @param <B> the type of the second unit in the result 017 */ 018public class MultUnit<A extends Unit, B extends Unit> extends Unit { 019 private final A m_unitA; 020 private final B m_unitB; 021 022 @SuppressWarnings("rawtypes") 023 private static final CombinatoryUnitCache<Unit, Unit, MultUnit> cache = 024 new CombinatoryUnitCache<>(MultUnit::new); 025 026 /** 027 * Creates a new product unit. Consider using {@link #combine} instead of manually calling this 028 * constructor. 029 * 030 * @param a the first unit of the product 031 * @param b the second unit of the product 032 */ 033 private MultUnit(A a, B b) { 034 super( 035 a.isBaseUnit() && b.isBaseUnit() ? null : combine(a.getBaseUnit(), b.getBaseUnit()), 036 a.toBaseUnits(1) * b.toBaseUnits(1), 037 a.name() + "-" + b.name(), 038 a.symbol() + "*" + b.symbol()); 039 m_unitA = a; 040 m_unitB = b; 041 } 042 043 /** 044 * Creates a new product unit. Subclasses of {@code MultUnit} should use this constructor. 045 * 046 * @param baseUnit the base unit. Set this to null if the unit being constructed is its own base 047 * unit 048 * @param a the first unit of the product 049 * @param b the second unit of the product 050 */ 051 protected MultUnit(MultUnit<A, B> baseUnit, A a, B b) { 052 super( 053 baseUnit, 054 a.toBaseUnits(1) * b.toBaseUnits(1), 055 a.name() + "-" + b.name(), 056 a.symbol() + "*" + b.symbol()); 057 m_unitA = a; 058 m_unitB = b; 059 } 060 061 MultUnit( 062 MultUnit<A, B> baseUnit, 063 UnaryFunction toBaseConverter, 064 UnaryFunction fromBaseConverter, 065 String name, 066 String symbol) { 067 super(baseUnit, toBaseConverter, fromBaseConverter, name, symbol); 068 m_unitA = getBaseUnit().unitA(); 069 m_unitB = getBaseUnit().unitB(); 070 } 071 072 /** 073 * Creates a new MultUnit unit derived from two arbitrary units multiplied together. 074 * 075 * <pre> 076 * MultUnit.combine(Volts, Meters) // Volt-Meters 077 * </pre> 078 * 079 * @param <A> the type of the first unit 080 * @param <B> the type of the second unit 081 * @param a the first unit 082 * @param b the second unit 083 * @return the combined unit 084 */ 085 @SuppressWarnings("unchecked") 086 public static <A extends Unit, B extends Unit> MultUnit<A, B> combine(A a, B b) { 087 return cache.combine(a, b); 088 } 089 090 /** 091 * {@inheritDoc} 092 * 093 * <p>Note: When called on an object of type {@code MultUnit} (and <i>not</i> a subclass!), this 094 * method will always return a {@link edu.wpi.first.units.measure.Mult} instance. If you want to 095 * avoid casting, use {@link #ofNativeBaseUnits(double)} that returns a {@code Per} instance 096 * directly. 097 * 098 * @param magnitude the magnitude of the measure in terms of its base units. 099 * @return the measurement object 100 */ 101 @Override 102 public Measure<? extends MultUnit<A, B>> of(double magnitude) { 103 return ofNative(magnitude); 104 } 105 106 /** 107 * {@inheritDoc} 108 * 109 * <p>Note: When called on an object of type {@code MultUnit} (and <i>not</i> a subclass!), this 110 * method will always return a {@link edu.wpi.first.units.measure.Mult} instance. If you want to 111 * avoid casting, use {@link #ofNativeBaseUnits(double)} that returns a {@code Per} instance 112 * directly. 113 * 114 * @param baseUnitMagnitude the magnitude of the measure in terms of its base units. 115 * @return the measurement object 116 */ 117 @Override 118 public Measure<? extends MultUnit<A, B>> ofBaseUnits(double baseUnitMagnitude) { 119 return ofNativeBaseUnits(baseUnitMagnitude); 120 } 121 122 /** 123 * Creates a new immutable measurement of the given magnitude in terms of this unit. This will 124 * always return a {@code Mult} object and cannot be overridden by subclasses. 125 * 126 * @param magnitude the magnitude of the measurement. 127 * @return the measurement object 128 * @see #of(double) 129 */ 130 public final Mult<A, B> ofNative(double magnitude) { 131 return new Mult<>(magnitude, toBaseUnits(magnitude), this); 132 } 133 134 /** 135 * Creates a new immutable measurement of the given magnitude in terms of the unit's base unit. 136 * This will always return a {@code Mult} object and cannot be overridden by subclasses. 137 * 138 * @param baseUnitMagnitude the magnitude of the measure in terms of its base units. 139 * @return the measurement object 140 * @see #ofBaseUnits(double) 141 */ 142 public final Mult<A, B> ofNativeBaseUnits(double baseUnitMagnitude) { 143 return new Mult<>(fromBaseUnits(baseUnitMagnitude), baseUnitMagnitude, this); 144 } 145 146 @Override 147 @SuppressWarnings("unchecked") 148 public Measure<? extends MultUnit<A, B>> zero() { 149 return (Measure<? extends MultUnit<A, B>>) super.zero(); 150 } 151 152 @Override 153 @SuppressWarnings("unchecked") 154 public Measure<? extends MultUnit<A, B>> one() { 155 return (Measure<? extends MultUnit<A, B>>) super.one(); 156 } 157 158 @Override 159 public Unit per(TimeUnit time) { 160 return VelocityUnit.combine(this, time); 161 } 162 163 /** 164 * Converts a measurement value in terms of another unit to this unit. 165 * 166 * @param magnitude the magnitude of the measurement in terms of the other unit 167 * @param otherUnit the other unit 168 * @return the value of the measurement in terms of this unit 169 */ 170 public double convertFrom(double magnitude, MultUnit<A, B> otherUnit) { 171 return fromBaseUnits(otherUnit.toBaseUnits(magnitude)); 172 } 173 174 @Override 175 @SuppressWarnings("unchecked") 176 public MultUnit<A, B> getBaseUnit() { 177 return (MultUnit<A, B>) super.getBaseUnit(); 178 } 179 180 /** 181 * Gets the first unit of the product. 182 * 183 * @return the first unit 184 */ 185 public A unitA() { 186 return m_unitA; 187 } 188 189 /** 190 * Gets the second unit of the product. 191 * 192 * @return the second unit 193 */ 194 public B unitB() { 195 return m_unitB; 196 } 197 198 @Override 199 public String toString() { 200 return "(" + m_unitA.toString() + " * " + m_unitB.toString() + ")"; 201 } 202 203 @Override 204 public boolean equals(Object o) { 205 if (this == o) { 206 return true; 207 } 208 if (o == null || getClass() != o.getClass()) { 209 return false; 210 } 211 if (!super.equals(o)) { 212 return false; 213 } 214 MultUnit<?, ?> multUnit = (MultUnit<?, ?>) o; 215 return Objects.equals(m_unitA, multUnit.m_unitA) && Objects.equals(m_unitB, multUnit.m_unitB); 216 } 217 218 @Override 219 public int hashCode() { 220 return Objects.hash(super.hashCode(), m_unitA, m_unitB); 221 } 222}