001/** 002 * The MIT License (MIT) 003 * 004 * Copyright (c) 2015-2016 decimal4j (tools4j), Marco Terzer 005 * 006 * Permission is hereby granted, free of charge, to any person obtaining a copy 007 * of this software and associated documentation files (the "Software"), to deal 008 * in the Software without restriction, including without limitation the rights 009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010 * copies of the Software, and to permit persons to whom the Software is 011 * furnished to do so, subject to the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be included in all 014 * copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 022 * SOFTWARE. 023 */ 024package org.decimal4j.base; 025 026import java.math.RoundingMode; 027 028import org.decimal4j.api.Decimal; 029import org.decimal4j.api.ImmutableDecimal; 030import org.decimal4j.arithmetic.Exceptions; 031import org.decimal4j.scale.ScaleMetrics; 032import org.decimal4j.scale.Scales; 033 034/** 035 * Base class for immutable {@link Decimal} classes of different scales. 036 * 037 * @param <S> 038 * the scale metrics type associated with this decimal 039 * @param <D> 040 * the concrete class implementing this {@code ImmutableDecimal} 041 */ 042@SuppressWarnings("serial") 043abstract public class AbstractImmutableDecimal<S extends ScaleMetrics, D extends AbstractImmutableDecimal<S, D>> 044 extends AbstractDecimal<S, D>implements ImmutableDecimal<S> { 045 046 private final long unscaled; 047 048 /*Used to store the string representation, if computed */ 049 private transient String stringCache; 050 051 /** 052 * Constructor with unscaled value. 053 * 054 * @param unscaled 055 * the unscaled value 056 */ 057 public AbstractImmutableDecimal(long unscaled) { 058 this.unscaled = unscaled; 059 } 060 061 @Override 062 public final long unscaledValue() { 063 return unscaled; 064 } 065 066 @Override 067 public ImmutableDecimal<?> scale(int scale) { 068 return scale(scale, RoundingMode.HALF_UP); 069 } 070 071 @Override 072 @SuppressWarnings("hiding") 073 public <S extends ScaleMetrics> ImmutableDecimal<S> scale(S scaleMetrics) { 074 return scale(scaleMetrics, RoundingMode.HALF_UP); 075 } 076 077 @Override 078 public ImmutableDecimal<?> scale(int scale, RoundingMode roundingMode) { 079 final int myScale = getScale(); 080 if (scale == myScale) { 081 return this; 082 } 083 final ScaleMetrics targetMetrics = Scales.getScaleMetrics(scale); 084 try { 085 final long targetUnscaled = targetMetrics.getArithmetic(roundingMode).fromUnscaled(unscaled, myScale); 086 return getFactory().deriveFactory(targetMetrics).valueOfUnscaled(targetUnscaled); 087 } catch (IllegalArgumentException e) { 088 throw Exceptions.newArithmeticExceptionWithCause("Overflow: cannot convert " + this + " to scale " + scale, 089 e); 090 } 091 } 092 093 @Override 094 @SuppressWarnings("hiding") 095 public <S extends ScaleMetrics> ImmutableDecimal<S> scale(S scaleMetrics, RoundingMode roundingMode) { 096 if (scaleMetrics == getScaleMetrics()) { 097 @SuppressWarnings("unchecked") 098 // safe: we know it is the same scale metrics 099 final ImmutableDecimal<S> self = (ImmutableDecimal<S>) this; 100 return self; 101 } 102 try { 103 final long targetUnscaled = scaleMetrics.getArithmetic(roundingMode).fromUnscaled(unscaled, getScale()); 104 return getFactory().deriveFactory(scaleMetrics).valueOfUnscaled(targetUnscaled); 105 } catch (IllegalArgumentException e) { 106 throw Exceptions.newArithmeticExceptionWithCause( 107 "Overflow: cannot convert " + this + " to scale " + scaleMetrics.getScale(), e); 108 } 109 } 110 111 @Override 112 public ImmutableDecimal<?> multiplyExact(Decimal<?> multiplicand) { 113 final int targetScale = getScale() + multiplicand.getScale(); 114 if (targetScale > Scales.MAX_SCALE) { 115 throw new IllegalArgumentException("sum of scales in exact multiplication exceeds max scale " 116 + Scales.MAX_SCALE + ": " + this + " * " + multiplicand); 117 } 118 try { 119 final long unscaledProduct = getDefaultCheckedArithmetic().multiplyByLong(unscaled, 120 multiplicand.unscaledValue()); 121 return getFactory().deriveFactory(targetScale).valueOfUnscaled(unscaledProduct); 122 } catch (ArithmeticException e) { 123 throw new ArithmeticException("Overflow: " + this + " * " + multiplicand); 124 } 125 } 126 127 @Override 128 public ImmutableDecimal<S> min(ImmutableDecimal<S> val) { 129 return isLessThanOrEqualTo(val) ? this : val; 130 } 131 132 @Override 133 public ImmutableDecimal<S> max(ImmutableDecimal<S> val) { 134 return isGreaterThanOrEqualTo(val) ? this : val; 135 } 136 137 @Override 138 public final String toString() { 139 String s = stringCache; 140 if (s == null) { 141 stringCache = s = getDefaultArithmetic().toString(unscaledValue()); 142 } 143 return s; 144 } 145}