001/*
002 * Units of Measurement Implementation for Java SE
003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil, V2COM.
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-363 nor the names of its 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 tec.uom.se;
031
032import javax.measure.UnitConverter;
033
034import tec.uom.se.function.Converter;
035
036import java.io.Serializable;
037import java.math.BigDecimal;
038import java.math.MathContext;
039import java.util.ArrayList;
040import java.util.List;
041import java.util.Objects;
042
043/**
044 * <p>
045 * The base class for our {@link UnitConverter} implementations.
046 * </p>
047 *
048 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
049 * @author <a href="mailto:units@catmedia.us">Werner Keil</a>
050 * @version 1.0, August 9, 2016
051 * @since 1.0
052 */
053public abstract class AbstractConverter implements UnitConverter, Converter<Number, Number>, Serializable {
054
055  /**
056   *
057   */
058  private static final long serialVersionUID = 5790242858468427131L;
059
060  /**
061   * The ratio of the circumference of a circle to its diameter.
062   **/
063  protected static final double PI = 3.1415926535897932384626433832795;
064
065  /**
066   * Holds identity converter.
067   */
068  public static final AbstractConverter IDENTITY = new Identity();
069
070  /**
071   * DefaultQuantityFactory constructor.
072   */
073  protected AbstractConverter() {
074  }
075
076  /**
077   * Concatenates this physics converter with another physics converter. The resulting converter is equivalent to first converting by the specified
078   * converter (right converter), and then converting by this converter (left converter).
079   *
080   * @param that
081   *          the other converter.
082   * @return the concatenation of this converter with that converter.
083   */
084  public AbstractConverter concatenate(AbstractConverter that) {
085    return (that == IDENTITY) ? this : new Pair(this, that);
086  }
087
088  @Override
089  public boolean isIdentity() {
090    return false;
091  }
092
093  @Override
094  public abstract boolean equals(Object cvtr);
095
096  @Override
097  public abstract int hashCode();
098
099  @Override
100  public abstract AbstractConverter inverse();
101
102  @Override
103  public UnitConverter concatenate(UnitConverter converter) {
104    return (converter == IDENTITY) ? this : new Pair(this, converter);
105  }
106
107  @Override
108  public List<? extends UnitConverter> getConversionSteps() {
109    List<AbstractConverter> converters = new ArrayList<>();
110    converters.add(this);
111    return converters;
112  }
113
114  /**
115   * @throws IllegalArgumentException
116   *           if the value is </code>null</code>.
117   */
118  public Number convert(Number value) {
119    if (value instanceof BigDecimal) {
120      return convert((BigDecimal) value, MathContext.DECIMAL128);
121    }
122    if (value != null) {
123      return convert(value.doubleValue());
124    } else {
125      throw new IllegalArgumentException("Value cannot be null");
126    }
127  }
128
129  @Override
130  public abstract double convert(double value);
131
132  public abstract BigDecimal convert(BigDecimal value, MathContext ctx) throws ArithmeticException;
133
134  /**
135   * This class represents the identity converter (singleton).
136   */
137  private static final class Identity extends AbstractConverter {
138
139    /**
140                 * 
141                 */
142    private static final long serialVersionUID = -4460463244427587361L;
143
144    @Override
145    public boolean isIdentity() {
146      return true;
147    }
148
149    @Override
150    public Identity inverse() {
151      return this;
152    }
153
154    @Override
155    public double convert(double value) {
156      return value;
157    }
158
159    @Override
160    public BigDecimal convert(BigDecimal value, MathContext ctx) {
161      return value;
162    }
163
164    @Override
165    public UnitConverter concatenate(UnitConverter converter) {
166      return converter;
167    }
168
169    @Override
170    public boolean equals(Object cvtr) {
171      return (cvtr instanceof Identity);
172    }
173
174    @Override
175    public int hashCode() {
176      return 0;
177    }
178
179    @Override
180    public boolean isLinear() {
181      return true;
182    }
183  }
184
185  /**
186   * This class represents converters made up of two or more separate converters (in matrix notation <code>[pair] = [left] x [right]</code>).
187   */
188  public static final class Pair extends AbstractConverter implements Serializable {
189
190    /**
191                 * 
192                 */
193    private static final long serialVersionUID = -123063827821728331L;
194
195    /**
196     * Holds the first converter.
197     */
198    private final UnitConverter left;
199
200    /**
201     * Holds the second converter.
202     */
203    private final UnitConverter right;
204
205    /**
206     * Creates a pair converter resulting from the combined transformation of the specified converters.
207     *
208     * @param left
209     *          the left converter, not <code>null</code>.
210     * @param right
211     *          the right converter.
212     * @throws IllegalArgumentException
213     *           if either the left or right converter are </code> null</code>
214     */
215    public Pair(UnitConverter left, UnitConverter right) {
216      if (left != null && right != null) {
217        this.left = left;
218        this.right = right;
219      } else {
220        throw new IllegalArgumentException("Converters cannot be null");
221      }
222    }
223
224    @Override
225    public boolean isLinear() {
226      return left.isLinear() && right.isLinear();
227    }
228
229    @Override
230    public boolean isIdentity() {
231      return false;
232    }
233
234    @Override
235    public List<UnitConverter> getConversionSteps() {
236      final List<UnitConverter> steps = new ArrayList<>();
237      List<? extends UnitConverter> leftCompound = left.getConversionSteps();
238      List<? extends UnitConverter> rightCompound = right.getConversionSteps();
239      steps.addAll(leftCompound);
240      steps.addAll(rightCompound);
241      return steps;
242    }
243
244    @Override
245    public Pair inverse() {
246      return new Pair(right.inverse(), left.inverse());
247    }
248
249    @Override
250    public double convert(double value) {
251      return left.convert(right.convert(value));
252    }
253
254    @Override
255    public BigDecimal convert(BigDecimal value, MathContext ctx) {
256      if (right instanceof AbstractConverter) {
257        return ((AbstractConverter) left).convert(((AbstractConverter) right).convert(value, ctx), ctx);
258      }
259      return (BigDecimal) left.convert(right.convert(value));
260    }
261
262    @Override
263    public boolean equals(Object obj) {
264      if (this == obj) {
265        return true;
266      }
267      if (obj instanceof Pair) {
268        Pair that = (Pair) obj;
269        return Objects.equals(left, that.left) && Objects.equals(right, that.right);
270      }
271      return false;
272    }
273
274    @Override
275    public int hashCode() {
276      return Objects.hash(left, right);
277    }
278
279    public UnitConverter getLeft() {
280      return left;
281    }
282
283    public UnitConverter getRight() {
284      return right;
285    }
286  }
287}